调试的基本步骤:

1.发现错误的存在

2.以隔离,消除等方式对错误进行定位

3.确定错误产生的原因

4.提出纠正错误的解决方法

5.对程序错误予以改正,重新测试


debug和release


c语言调试技巧_c

debug:调试版本,保存调试信息,不做任何优化,便于程序员调试


release:发布版本,往往进行了各种优化,是程序在代码大小和运行速度得到最优,以便用户更好地使用


【例子】

如下代码在debug和release下运行结果为:

#include <stdlib.h>
#include<stdio.h>
int main()
{
int i = 0;
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
for (i = 0; i <= 12; i++)
{
printf("死循环");
arr[i] = 0;
}
system("pause");
return 0;
}

debug下:

c语言调试技巧_c_02release下:

c语言调试技巧_c_03



学习调试:

F5:启动调试----与F9配合使用

F9:创建和取消,切换断点

F10:逐过程,每次处理一个过程

F11:逐语句,每次执行一条语句

shift+F11:跳出

ctrl+F5:开始执行,不调试

shift+F5:停止调试


查看调试信息:

监视,内存,调用堆栈,局部变量,自动窗口,寄存器等


实例一:

​代码实现:求1!+2!+3!...+n!;不考虑溢出



int main()
{
int i = 0;
int sum = 0;
int n = 0;
int ret = 1;
scanf("%d", &n);
for (i = 1; i <= n; i++)
{
int j = 0;
for (j = 1; j <= i; j++)
{
ret *= j;
}
sum += ret;
}
printf("%d", sum);

return 0;
}


输入 3;

得到错误结果:15 


按F5调试,发现问题;

c语言调试技巧_c_04

打断点127行,并设置条件

c语言调试技巧_c_05

断点条件设置为第三次i循环

c语言调试技巧_c_06

发现第三次j循环 ret初始值为2

c语言调试技巧_c_07

发现j循环第三次结束 ret 不对

c语言调试技巧_c_08

发现问题:ret循环后未初始化

改正问题:增加“ret = 1;” 赋值语句

修改后代码:


int main()
{
int i = 0;
int sum = 0;
int n = 0;
int ret = 1;
scanf("%d", &n);
for (i = 1; i <= n; i++)
{
int j = 0;

ret = 1;

for (j = 1; j <= i; j++)
{
ret *= j;
}
sum += ret;
}
printf("%d", sum);

return 0;
}



实例二


#include <stdlib.h>
int main()
{
int i = 0;
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
for (i = 0; i <= 12; i++)
{
printf("死循环\n");
arr[i] = 0;
}
system("pause");
return 0;
}

运行结果:

c语言调试技巧_c_02


开始调试:


按F5,打开监视,输入监视参数;

c语言调试技巧_c_10


c语言调试技巧_c_11

【debug情况下 栈示意图】

高地址


i的地址



a[9]

.

.

.

.

.

.

a[2]

a[1]

a[0]


低地址


如图,此时,当a[12]被赋值为0时,正好修改的为i地址的值,i也变为0,i自增后,程序陷入死循环

此时,在此vs2013编译环境下,如果修改代码i<=11,也可勉强修改错误;

在release版本下,却能运行,这是因为程序进行自我优化后,会优先定义数组arr,然后定义i,此时在栈中,i的地址比arr地址更低,则可避免死循环。



【如何写出易于调试的代码】

​1.代码运行正常

2.bug很少

3.效率高

4.可读性高

5.可维护性高

6.注释清晰

7.文档齐全


常见的coding技巧

​1.使用assert(断言

2.尽量使用const

3.养成良好的编码风格

4.添加必要的注释

5.避免代码的陷阱


示例:模拟实现库函数strcpy


//【优化代码】
#include<assert.h>

char* my_strcpy(char* x,const char* y)//const保护原数据
{
char* ret = x;
assert(x != NULL);//断言 如果为真什么都不发生,如果为假报错
assert(y != NULL);
//把y指向的字符串拷贝到x指向的空间,包含‘\0’

while (*x++ = *y++)
{
;
}
return ret;
}

int main()
{
char arr1[] = "AAAAAAAAAAA";
char arr2[] = "123";
my_strcpy(arr1, arr2);
printf("%s\n", my_strcpy(arr1,arr2));
return 0;
}

“const”的使用,增强代码的强壮度










0