网站域名实名证明,打电话沟通做网站,郑州网站开发招聘,版面设计图大全简单又漂亮 ✨✨ 欢迎大家来到贝蒂大讲堂✨✨ #x1f388;#x1f388;养成好习惯#xff0c;先赞后看哦~#x1f388;#x1f388; 所属专栏#xff1a;C语言学习 贝蒂的主页#xff1a;Betty‘s blog 1. 什么是调试
当我们写代码时候常常会遇见输出结果不符合我们预… ✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 养成好习惯先赞后看哦~ 所属专栏C语言学习 贝蒂的主页Betty‘s blog 1. 什么是调试
当我们写代码时候常常会遇见输出结果不符合我们预期的情况那这时候我们该怎么办呢
这时候我们就需要调试(debug)调试简单来说就是去寻找问题找到错误原因修改代码的过程。 2. Debug和Release 在VS编译器中有着这Debug和Release两个选项他们分别是调试版本与发布版本那这两者有什么区别么我们可以看看下表
名称优点缺点Debug包含调试信息易于调试未做任何优化体积大Release体积小编译时对应用程序的速度进行优化无法调试 Debug文件与Release文件(需要代码运行才生成) 通过观察我们可以知晓Release生成的应用文件要比Debug生成的应用文件小的多 3. 调试快捷键
我们在调试过程中可以使用一些快捷键帮助我们节省时间。
下表列出了比较常用的快捷键以及其功能
快捷键功能F5启动调试经常⽤来直接跳到下⼀个断点处⼀般是和F9配合使⽤。F9创建断点和取消断点F10逐过程通常⽤来处理⼀个过程⼀个过程可以是⼀次函数调⽤或者是⼀条语句。F11逐语句就是每次都执⾏⼀条语句但是这个快捷键可以使我们的执⾏逻辑进⼊函数内部。在函数调⽤的地⽅想进⼊函数观察细节必须使⽤F11如果使⽤F10直接完成函数调⽤。CTRLF5开始执⾏不调试如果你想让程序直接运⾏起来⽽不调试就可以直接使⽤。
断点的作⽤是可以在程序的任意位置设置断点打上断点就可以使得程序执⾏到想要的位置暂定执⾏接下来我们就可以使⽤F10F11这些快捷键观察代码的执⾏细节。 4. 监视和内存观察
4.1 监视
在调试的过程中我们如果要观察代码执⾏过程中上下⽂环境中的变量的值这时候就要用到监视 比如我们要监视下面这段代码
int main()
{int i 0;for (i 0; i 10; i){printf(%d , i);}return 0;
}打开监视(提前F11启动调试) 监视变量
通过监视变量的变化能够更好的发现错误信息 4.2 内存
除了探究变量改变状况我们还可以探究变量在内存中的存储
我们还是以上面那段代码举例
打开内存(提前F11启动调试) 内存信息 5. 调试举例
5.1 实例一简单调试 计算 1!2!3!的和 错误代码
int main()
{int i 0;int ret 1;int sum 0;for (i 1; i 3; i){for (int j 1; j i; j){ret * j;}sum ret;}printf(%d , sum);return 0;
}输出结果 15 有时候我们不能一下子看出错误这时候我们就需要调试 第一步分析 1!1 sum1 2!2 sum3 3!6 sum9 监控变量并调试分析 第一步没问题F10继续调试第二步没问题F10继续调试第三步有问题增加变量继续分析
为什么ret的值会出现不符合预期的情况呢我们再次观察代码就会发现ret的值在每次使用后都没有更新所以出现不符合预期的情况
正确代码
int main()
{int i 0;int ret 1;int sum 0;for (i 1; i 3; i){ret 1;//更新for (int j 1; j i; j){ret * j;}sum ret;}printf(%d , sum);return 0;
}5.2 实例二深度理解
在VS2022、X86、Debug的环境下下⾯代码输出结果是什么
#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){arr[i] 0;printf(betty\n);}return 0;
}输出结果死循环打印betty 为什么会出现下面结果呢这段代码不是越界访问了吗这时我们又要调试起来
数组未越界时并没有发现问题 数组越界时我们发现当arr[12]0时i也变为0这也是为什么代码会死循环的原因 为什么改变arr[12]的值i也随之改变呢我们猜测i与arr[12]的地址相同 事实也证明我们的猜测是正确的
那为什么会这样呢我们要知道以下三点
局部变量一般存储在内存中的栈区栈区内存的使⽤习惯是从⾼地址向低地址使⽤的所以变量i的地址是较⼤的。arr数组的地址整体是⼩于i的地址。数组在内存中的存放是随着下标的增⻓地址是由低到⾼变化的。
图像演示如下 原因在该环境下arr与i的地址直接刚好差两个整型当越界访问到arr[12]时刚好与i的地址重合所以改变arr[12]的值也会改变i的值代码也就会死循环
注意 在不同的编译器下可能arr与i空出的空间⼤⼩是不⼀样的代码中这些变量内存的分配和地址分配是编译器指定的所以的 不同的编译器之间就有差异了。所以这个题⽬是和环境相关的。 栈区的默认的使⽤习惯是先使⽤⾼地址再使⽤低地址的空间但是这个具体还是要编译器的实现⽐如在VS上切换到X64这个使⽤的顺序就是相反的在Release版本的程序中这个使⽤的顺序也是相反的。
5.3 实例三断点调试
我们知道了简单调试该如何去调那断点调试该如何去进行呢
我们以下面这段代码举例
int main()
{int i 0;for (i 0; i 100; i)//第一步{printf(Betty );}int arr[10] { 0 };for (i 0; i 10; i)//第二步{//.....}return 0;
}如果我们可以确定第一步没有问题问题出现在第二步如果一步一步调试的话要调试100次才会到第二步所以这时候我们就需要用到断点 首先我们选中要调试的行数按下F9打下断点然后F5开始调试 如果觉得第一步i50时有问题也可以在第一步设置断点然后右击断点设置条件 6. 编译器报错
6.1 编译型错误
编译型错误⼀般都是语法错误这类错误⼀般看错误信息就能找到⼀些蛛丝⻢迹双击错误信息也能初步的跳转到代码错误的地⽅或者附近。 6.2 链接型错误
链接型错误一般是因为标识符名不存在拼写错误头文件未包含引⽤的库不存在 6.3 运⾏时错误
运行时错误复杂多样一般需要借助调试的手段才能发现