你不知道的VS调试技巧,来这看就对了(下)

简介: 笔记

如何写出好(易于调试)的代码


1. 使用assert

2. 尽量使用const

3. 养成良好的编码风格

4. 添加必要的注释

5. 避免编码的陷阱


实现strcpy函数

1.png

虽然功能实现了,但是还有需要优化的地方

2.png

上面的代码,还能再优化,如果有人传了个空指针,则达不到我们想要的效果

3.png

     这个时候我们可以用,assert进行断言,此时程序会报错

4.png5.png6.png

当传arr2,此时未报错,但还能继续优化,当有人把这俩者弄反时,还会报错

7.png

我们的目的不是改变逗号后面的内容,目的是改变逗号前面的,我们给第二个加上const,若运行,则报错

8.png

const作用


//int main()
//{
//  //int num = 10;
//  //num = 20;
//  //printf("%d\n", num);
//
//  const int num = 10;
//  //num = 20;
//  //const 修饰指针变量
//  //1. const 放在*的左边
//  //意思是:p指向的对象不能通过p来改变了,但是p变量本身的值是可以改变的
//  //*p = 20;//err
//  //2. const 放在*的右边
//  //意思是:p指向的对象是可以通过p来改变的,但是不能修改p变量本身的值
//  //
//  //int* const p = #
//  意思
//  //*p = 0;//ok
//  //int n = 100;
//  //p = &n;//err
//
//  //const int* p = #
//  //int n = 100;
//
//  int const* p;
//  //*p = 20;//err
//  //p = &n;//ok
//
//  printf("%d\n", num);
//
//  return 0;
//}

总结:const修饰常变量,const修饰谁,谁就不能被直接改变,如const修饰a,不能直接改变a,但可拿指针变量改变a


模拟实现strlen函数


int my_strlen(const char* str)
{
  int count = 0;
  assert(str);
  while (*str != '\0')
  {
    count++;
    str++;
  }
  return count;
}
int main()
{
  char arr[] = "hello bit";
  int len = my_strlen(arr);//char*
  printf("%d\n", len);
  return 0;
}

练习题

如有以下代码:


struct student
{
  int num;
  char name[32];
  float score;
}stu;

则下面的叙述不正确的是:( )


A.struct 是结构体类型的关键字


B.struct student 是用户定义的结构体类型


C.num, score 都是结构体成员名


D.stu 是用户定义的结构体类型名


A:正确,在C语言中需要自定义类型时,要用到struct关键字


B:正确:在C语言中,用struct定义的结构体,定义结构体类型变量时,需要用struct student


C:正确:结构体中的变量名称,称之为结构体的成员


D:错误:stu是定义的结构体类型变量,不是名称,如果想要让stu为结构体类型名称时,必须在结构体定义时添加   typedef关键字


 


结构体访问成员的操作符不包含:( )


A.. 操作符


B.-> 操作符


C.* 解引用操作符


D.sizeof


A:正确,结构体类型变量访问结构体中成员时,使用.操作符


B:正确,指向结构体类型变量的指针访问结构体中成员时,使用->操作符


C:正确,指向结构体类型变量的指针也可以通过.方式访问成员,只不过要先通过*对该指针解引用


D:错误,sizeof是求结构体类型大小的


因此:选择D


关于二级指针描述描述正确的是:( )


A.二级指针也是指针,只不过比一级指针更大


B.二级指针也是指针,是用来保存一级指针的地址


C.二级指针是用来存放数组的地址


D.二级指针的大小是4个字节


A:错误,二级指针是指针,不能说起比一级指针大,只能说二级指针指向的空间中存储的也是一个地址


B:正确


C:错误,数组的地址一般用一级指针存储,或者用数组指针接收


D:二级指针是指针,但是否占4个字节不一定,要看具体的系统


因此:选择B


下面哪个是指针数组:( )


A. int* arr[10];


B.int * arr[];


C.int **arr;


D.int (*arr)[10];


指针数组是一个数组,该数组的每个元素是一个指针


A:正确,定义了一个数组,该数组中有10个元素,每个元素都是int*的指针类型


B:错误,编译失败,定义数组时,要给出空间的大小,如果没有给时,必须要给出初始化结果


C:错误,定义了一个二级指针


D:错误,*和arr先结合,说明arr不是数组。实际上arr是一个指针,一个指向数组的指针。


arr是一个数组指针,后续会更新这一块


因此:选择A


下面程序要求输出结构体中成员a的数据,以下不能填入横线处的内容是( )


#include < stdio.h >
struct S
{ 
  int a;
  int b; 
};
int main( )
{ 
  struct S a, *p=&a;
  a.a = 99;
  printf( "%d\n", __________);
  return 0;
}

A.a.a


B.*p.a


C.p->a


D.(*p).a


结构体类型变量需要访问其成员时,用.运算符,如果是指向结构体变量的指针访问时,需要用->,或者先对指针解引用,取到指向的结构体类型的变量,再通过.访问,但是要注意优先级


.的优先级大于*,先形成p.a,然后再形成*p.a,因为p是指针,不能用.要用->,所以错误


下面程序的输出结果是:( )


struct stu
{
    int num;
    char name[10];
    int age;
};
void fun(struct stu *p)
{
  printf(“%s\n”,(*p).name);
  return;
}
int main()
{
  struct stu students[3] = {{9801,”zhang”,20},
         {9802,”wang”,19},
                              {9803,”zhao”,18}
                             };
    fun(students + 1);
  return 0;
}


在main函数中先定义了一个stu结构体类型的数组students,students指向结构体的起始位置,students+1表示该数组中的第一个元素,因此fun的形参实际指向的是students数组中的第一个元素,故打印的是wang


喝汽水,1瓶汽水1元,2个空瓶可以换一瓶汽水,给20元,可以多少汽水(编程实现)。

#include<stdio.h>
int main()
{
  int money;
  scanf("%d", &money);
  int s = 0;     
  int t = 0;
  int sum = money;
  while (money!=0)
  {
  if (money == 1)
  {
    s = money + t;
    t = 0;
  }
    sum = sum + s;
    if (money % 2 != 0&&money>1)
    {
      t++;
    }
    s=money = money / 2; 
  }
  printf("%d", sum);
  return 0;
}
#include<stdio.h>
int main()
{
  int money = 20;//总共有多钱
  int empty = 20;//空瓶个数
  int total = 20;//换了多少瓶
  while(empty>=2)           //空瓶大于等于俩个时,开始换
  {
  total = total + empty/2;//总瓶数=拿钱买的+空瓶/2
  empty = empty / 2 + empty % 2;//有时候会出现剩余一瓶的可能性,把这瓶加上
  }
  printf("%d", total);
  return 0;
}

Debug和Relase补充


9.png

这个是在Debug下运行的结果,会进入死循环, image.png

而在relase版本下我们可以看到,程序运行完毕,这是因为 relase版本对代码进行了优化,i的地址不再和arr[12]的地址重复

image.png


release版本下的地址

image.png

debug下的地址

 

我们发现地址不同,这是因为release版本对代码进行了优化

相关文章
|
7月前
|
编译器
实用调试技巧(2)
实用调试技巧(2)
|
8月前
|
搜索推荐 C++ 容器
你很可能需要知道这个调试小技巧
你很可能需要知道这个调试小技巧
|
5月前
|
安全 程序员 Windows
实用调试技巧
实用调试技巧
57 0
|
6月前
|
程序员 编译器
实用调试技巧(上)
实用调试技巧
|
7月前
|
程序员 Windows
实用调试技巧(1)
实用调试技巧(1)
|
8月前
|
C++
5 个非常实用的 vs 调试技巧
5 个非常实用的 vs 调试技巧
|
9月前
|
程序员 C语言
|
9月前
|
程序员 编译器
|
数据采集 安全 程序员
实用调试技巧——“C”
实用调试技巧——“C”