调试对程序员来说是必不可少的,没人能保证一辈子写的代码一个bug都没有。所以学会调试,很重要。
没有手绘封面了,apple pencil丢了。。。
1. 调试 1.1 调试是什么?
1.2 怎么调试?调试(Debugging / Debug),又称除错,是发现和减少计算机程序或电子仪器设备中程序
错误的一个过程
- 发现错误
- 通过 隔离 、消除 等方式,定位错误
- 确定错误原因
- 思考解决办法
- 实现解决办法,对程序除错
- 重新测试
Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。
Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优
的,以便用户很好地使用。
2. Windows下的调试 2.1 调试环境
2.2 调试快捷键在环境选项中选 Debug
F5
启动调试
常用于跳到下一个断点
F9
创建/取消断点
让程序在你希望的地方(至少是有意义的代码处)停下来
F10
逐过程
过程:一次函数调用,一条语句,都是一个过程
F11
逐语句
一次只执行一条语句,常用于进入函数内部
2.3 调试的重要部分:“监视”Ctrl + F5
不调试,直接执行程序
开始调试后,在“调试” – “窗口” 里,可以任意找到想监视的信息
3. 调试实例 3.1 例1
#includeint main() { int i = 0; int n = 0; int sum = 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("%dn", sum); return 0; 结果:15 3! + 2! + 1! = 6+2+1 = 9 为什么得到15? }
对 ret 的需求是每次存储 n 的阶乘,但是调试发现,ret 的值一直累加,正确代码应该是这样:
#includeint main() { int i = 0; int n = 0; int sum = 0; scanf("%d", &n); for (i = 1; i <= n; i++) { int ret = 1; int j = 0; for (j = 1; j <= i; j++) { ret *= j; } sum += ret; } printf("%dn", sum); return 0; }
3.2 例2调试过程中,时刻观察程序走向是不是和脑中构思好的一致
#includeint main() { int arr[10] = { 0 }; int i = 0; for (i = 0; i <= 12; i++) { arr[i] = 0; printf("hehen"); } return 0; } 结果:死循环/报错(越界访问)
是的,这里越界访问了,但是根据 vs 的内存使用规则,越界访问的 arr[12] 刚好是 i ,每当程序要结束 , i 就又被赋成 0 ,导致死循环
vs在栈区使用空间,总是从高地址开始使用
vs数组的使用中,随着下标增加,地址由低到高
*静态区是右下角的方格,不知道怎么跑上去了 -_-
4. 如何写出好,还易于调试的代码? 4.1 优秀的代码
4.2 如何向优秀代码靠近?
- 运行正常
- bug很少
- 效率高
- 可读性高
- 可维护性高
- 注释明了
- 文档齐全
4.3 优秀代码示例(多向优秀代码学习)
- 使用assert(断言,我视它为保安)
- 尽量使用const
- 良好代码风格(适合的缩进,空格之类)
- 添加必要的注释(明了简洁)
- 避免代码的陷阱
#include4.4 const 的作用#include //************ //= char* my_strcpy( dst , src ) - Copy one string over another // //= Purpose: // Copies the string "src" into the spot // specified by dest ; assumes enough room. // //= Entry: // char* dst - string over which "src" is to be copied // const char* src - string to be copied over "dst" // // = Exit: // The address of "dst" //************** char* my_strcpy(char* dst, const char* src) { char* tool = dst; //两个形参都不为NULL assert(dst && src); while (*tool++ = *src++)// = 0 { ;//copy src over dst } return dst; } int main() { char str1[] = "Coding is funny!"; char str2[30] = { 0 }; printf("%sn", my_strcpy(str2, str1)); return 0; }
const:赋予常量属性
看看例子(参考前辈的例子)
#include//代码1 void test1() { int n = 10; int m = 20; int *p = &n; *p = 20;//ok? p = &m; //ok? } void test2() { //代码2 int n = 10; int m = 20; const int* p = &n; *p = 20;//ok? p = &m; //ok? } void test3() { int n = 10; int m = 20; int *const p = &n; *p = 20; //ok? p = &m; //ok? } int main() { //测试无cosnt的 test1(); //测试const放在*的左边 test2(); //测试const放在*的右边 test3(); return 0; }
总结:就是看 const 在谁的左边
- 在 * 左边,就是修饰指针变量指向的内容:
指针指向的值被赋予常量属性- 在 * 右边,就是修饰指针变量:
指针变量里的地址被赋予常量属性
5. 编程常见错误 5.1 编译型错误
5.2 链接型错误多是语法问题,双击错误信息,根据错误信息修正即可
5.3 运行时错误多是 标识符不存在 、 拼写错误 、 没有声明
5.4 通向 “无bug” 的捷径逐步调试,细心对照:“是否和脑中构思的相同?”
不断积累排错经验,两点之间线段最短,脚踏实地就是最快的捷径
今天的分享就到这里
培根的blog,不断进步
:粉丝、阅读量不是初衷,写博客是为了倒逼自己输出,提升自己,不浮躁,不虚荣



