12.0、C语言——实用调试技巧

12.0、C语言——实用调试技巧

调试的基本步骤:

        发现程序错误的存在        
        以隔离、消除等方式对错误进行定位
        确定错误产生的原因
        提出纠正错误的解决方法
        对程序错误予以改正,重新测试

Debug和Release的介绍:

Debug通常称为测试版本,它包含调试信息【所以从文件内存大小上看,Debug版本也比Release要大】,并且不作任何优化,便于程序员调试程序

Release称为发布版本,他往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便很好的使用【不能像Debug那样一行一行的执行调试】 

        写好代码点击运行后,会发现代码文件夹中多了一个Debug文件夹,点进去可以看到一个.exe文件,双击可以运行:
        那么当我们选择Release再运行一次代码后,会发现多了一个Release文件夹,里面也有一个.exe文件,双击之后可以运行~

 

学会使用快捷键:

vs最常用的几个快捷键:

        F5
 
      启动测试,经常用来直接调到下一个断点处,再按一下会调到执行逻辑的下一个断点处【通常与F9配合使用】

        F9

        创建断点和取消断点,断点的重要作用,可以在程序的任意位置设置断点,这样就可以使得程序在想要的位置随意停止执行,继而一步步执行下去【通常与F5配合使用】   

         F10

         逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句,【比如遇到函数的时候,不会进入到函数内部去,而是直接执行函数,然后返回继续向下执行】

         F11

        逐语句,就是每次都执行一条语句,但是这个快捷键可以使我们的逻辑 进入函数内部(这是最常用的),当然如果进入函数内部后想要跳出来用 shift + f11

        Ctrl + F5
        开始执行不调试,如果你想让程序直接运行起来而不调试就可以直接使用

 

下面来看一个程序:

int main() {
	int arr[10] = {1,2,3,4,5,6,7,8,9,10};
	for (int i = 0; i <= 12;i++) {
		printf("hehe\n");
		arr[i] = 0;
	}
	return 0;
}

运行后的结果是陷入死循环一直打印 hehe ,但是为什么呢?

首先来看一下在 该main()函数 中 ,栈空间的存储方式:
 

        那么存储的方式就是这么恰巧,数组 arr[] 分配的内存空间就在变量 i 空间的后面两个,那么当我们数组访问越界到第三次也就是 i = 12 的时候,刚好此时i的地址和 arr[12] 的地址相同,所以当我们将 arr[12] 置为 0 的时候同时也将 i 置为 0 了,导致程序陷入死循环;
        如果将 for 循环条件改为 i<=11 那么就不会陷入死循环而是报 访问越界的错误;
        如果将 for 循环条件改为 i<=13或者更大的数字也会陷入死循环,这是肯定的因为12就已经死循环了,比12大肯定也会进入死循环~

        【当然在不同的编译器中死循环的位置也不相同,因为每个编译器的的内存布局有所差异】

        在运行上述代码的时候用的是debug版本运行的;
当我们用release版本运行程序的时候,发现并不会出现死循环的现象,为什么呢?
        这是因为release帮我们做了一定的优化,通过查看栈空间的内存可以发现,release将变量 i 的定义放到了定义数组 arr 的后面,这样一来就不会陷入死循环~

assert断言:

我们先来写一个字符串拷贝的函数 my_strcpy() 如下:

void my_stycpy(char* ori,char* dest) {  //将原来的字符ori拷贝到目标字符数组dest
	assert(dest != NULL); //断言
	assert(ori != NULL); //断言
	while (*dest++ = *ori++) {
		;
	}
}

        在字符串拷贝之前先用 assert() 断言 判断一下这两个指针是否为空指针,如果为空指针就会抛出这个异常,让程序猿能够看见~方便调试~

        在使用 assert() 的时候记得引入头文件 #include <assert.h>

const 关键字修饰指针变量:

用 法 一:

int main() {
	int num = 10;
	const int* p = &num;
	*p = 20;
}

        当 const 放在 " * " 前面时,那么可以看做 const 修饰的是 *p , 所以第三条语句*p = 20; 是错误语句,不可以修改 *p 的值【此时修饰的是*p,限制的是 *p】

用 法 二:

int main() {
	int num = 10;
	int* const p = &num;
	int a = 20;
	p = &a;
}

        当 const 放在 " * " 后面时,那么可以看做 const 修饰的是 p , 所以第四条语句 p = &a; 是错误语句,不可以修改 p 的值【此时修饰的是指针变量本身,限制的是 p】

        那么给大家解释完 const 的用法过后,再来优化一下之前写好的 my_strcpy(const char* ori,char* dest) 函数,

char* my_stycpy(const char* ori,char* dest) {
	char* ret = dest;
	assert(dest != NULL); //断言
	assert(ori != NULL); //断言
	while (*dest++ = *ori++) {
		;
	}
	return ret;
}

int main() {
	char arr1[] = "###########";
	char arr2[] = "hello";
	printf("arr1[] = %s", my_stycpy(arr2, arr1));
}

        该函数是将原字符 ori 拷贝到 dest 目标字符上去,所以我们的原字符肯定是不能够被改变的,所以这里给 ori 指针变量加上 const 关键词修饰会后,*ori 则受到限制不能被改变了;
        那么最后返回值的部分也从 void 改成了 char*,返回的是目标字符数组的首地址,这样方便我们调用完 my_stycpy() 函数之后查看目标数组是否改变~

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值