文章目录
- Ⅰ 调试的介绍
- Ⅱ 常用调试快捷键
- Ⅲ 调试的时候查看程序当前信息
- ⒈查看临时变量的值
- ⒉查看内存信息
- ⒊查看调用堆栈
- ⒋查看汇编信息
- ⒌查看寄存器信息
- Ⅳ 观察形参指针指向的数组
- Ⅴ 易于调试的代码该如何编写
- ⒈const 修饰指针变量
- ⒉良好代码示范
- Ⅵ 编程中常见的错误
Ⅰ 调试的介绍
1. 什么是调试
- 调试(Debug),又称为除错,是发现和减少计算机程序或电子仪器设备中程序的错误的一个过程。
2. 调试的基本步骤
- 发现程序的错误的存在(开发 → 测试 → 用户)。
- 以隔离、消除等方式对错误进行定位。
- 确定错误产生的原因。
- 提出纠正错误的解决办法。
- 对程序错误允以改正,重新测试。
3. Debug 和 Release 的介绍
- Debug 称为调试版本,它包含有各种调试信息,且不作任何优化,所占空间更大。
- Release 称为发布版本,此版本进行了优化,不包含调试信息,用户使用的以及测试人员检测的都是这个版本。
4. 调试环境的准备
- 在环境中选择 Debug 选项,只有在这里才可以进行调试。
Ⅱ 常用调试快捷键
- 注意:以下所有快捷键如果不起作用的话,请搭配 FN 键使用。
F5
- 启动调试:用于直接跳到下一个断点处,经常和 F9 成对使用。
F9
- 创建断点和取消断点:断点的重要作用,可以在程序的任意位置设置断点。
- 这样就可以使得程序在想要的位置随意停止执行,从而一步步执行下去。
F10
- 逐过程调试:通常用于处理一个过程,一个过程可以是一次函数调用,或是一条语句。
- 当碰到函数调用时,不会进到函数内部,无法观测到函数体内部的细节。
F11
- 逐语句调试:每次都执行一条语句,可以进入函数体观察函数内部的细节。
CTRL + F5
- 开始执行不调试:用于让程序直接运行起来而不进行调试,学习编程初期使用的最多的就是这个。
Ⅲ 调试的时候查看程序当前信息
- 进入调试之后,在调试窗口中才能看到下面的信息。
⒈查看临时变量的值
- 在调试开始之后,用于观察变量内存放的值的变化。
- 自动窗口与监视窗口都能够观察到局部变量的信息。
自动窗口与监视窗口的区别
- 自动窗口会自动显示所有局部变量的信息(不管你是否需要),随着调试的进程,自动窗口内所观察的变量有时会自动出现,有时又会自动消失。
- 监视需要程序员手动输入(想观察谁就输入谁),观察的变量不会随着调试的进程而消失,强雷建议使用监视窗口来观察局部变量的值。
⒉查看内存信息
⒊查看调用堆栈
调用堆栈
- 调用堆栈,反应的是函数的调用关系。
- 后期当遇到很复杂的函数关系的时候,就可以使用调用堆栈来理清函数之间的调用关系。
举个栗子
- 现有以下代码:
- main 函数调用了 test1,test1调用了 test2,test2 → test3。
- 而 main 函数又是被其他函数所调用的。
- 函数调用采用的是数据结构中 栈 的先进后出的特性,先调用的函数后结束。
⒋查看汇编信息
- 查看 C 语言代码所翻译出来的汇编代码。
⒌查看寄存器信息
- 用于查看当前运行环境的寄存器的使用信息。
Ⅳ 观察形参指针指向的数组
- 众所周知,在给函数传数组名的时候,传递的是数组首元素的地址;
- 这样一来的话,在函数内部调试的时候,只是输入个数组名就不能很好的观察到数组内部的值的变化。
- 但是,只要在我们想要观察的数组指针变量名后面加上 " 逗号 " 和 " 想要观察的元素个数 " 就可以很好观察到数组内部元素的值了。
二维数组同理
- 如果将二维数组名作为参数传给函数的话,观察函数形参的二维数组指针也就只能观察到原数组第一行的内容。
- 因为二维数组的数组名为第一行的地址。
Ⅴ 易于调试的代码该如何编写
1. 什么是高质量的代码
- 代码能够正常运行。
- 尽可能少的 BUG(不可能没有BUG)。
- 可执行效率够高。
- 可读性要强。
- 可维护性高。
- 注释要清晰,能让人看得懂。
- 相应文档齐全。
2. 常用编程技巧
- 使用 assert 断言。
- assert 的表达式如果为假则直接结束程序并报错,使用 assert 来判断指针是否指向 NULL。
- 尽量使用 const 修饰变量(包括变量和指针变量)。
- 养成良好的编码风格。
- 添加必要的注释。
- 避免编码的陷阱。
⒈const 修饰指针变量
- const 可以修饰普通的变量,让变量的值无法被修改,从而变成常变量(本质任然是个变量)。
-
同样的,const 也可以用来修饰指针变量。const 修饰指针有两种形式。
-
对于指针变量来说,应该关注的有两点:
- 指针变量本身能不能改;
- 指针变量指向的那个变量能不能改。
-
const 放置的位置就决定了能不能修改这两种值。
1. const 放在 * 的左边
const int* p = &n;
//只要 const 放在 * 的左边,这两中写法就都一样。
int const* p = &n;
- 限制的是指针指向的内容,也就是限制了 *p,让指针指向的那个变量的值无法被修改。
- 但是指针变量是可以修改的,指针可以指向其他变量。
2. const 放在 * 的右边
int* const p = &n;
- 限制的是指针变量本身,在 p 已经指向了 n 之后,p 就不能再被修改为指向其他变量了。
- 但是可以通过指针变量修改指向的内容。
- 当在 * 的两边都加上 const 之后,p 就只能指向 n,并且*p 也不能改变 n 的值了。
⒉良好代码示范
模式实现 strcpy
#include <stdio.h>
#include <assert.h>
//我只想改变 str1,不想改变 str2 ,const 保证 src 指针指向的 str2 数组内的值不会一不小心被修改
char* my_strcpy(char* dest, const char* src)
{
assert(dest && src); //当传过来的两个指针为 NULL 时,直接结束程序并报错
char* ret = dest; //保存目的地首元素的地址,一会还要传回去
while (*dest++ = *src++) //直到 *src = \0,然后由于是后置++,再将 \0 也赋值给 dest 为止
{
;
}
return ret; //返回 dest 的起始地址
}
int main()
{
char str1[20] = "***************";
char str2[] = "hello word!";
printf("%s\n", my_strcpy(str1, str2));
return 0;
}
模拟实现 strlen
#include <stdio.h>
#include <assert.h>
size_t my_strlen(const char* str) //防止原数组中得内容不小心被修改
{
assert(str); //判断传过来的指针的有效性 (!= NULL)
const char* end = str; //标识字符串末尾
while (*end++); //当 end 指向 \0 时退出循环
return (end - str - 1); //指针 - 指针得到同一块空间的两个地址之间的元素个数
//-1 是因为后置 ++ 的副作用,要把 \0 的那一个元素去掉
}
int main()
{
char str[] = "hello word!";
printf("%zd\n", my_strlen(str));
return 0;
}
Ⅵ 编程中常见的错误
1. 编译型错误
- 一般为语法错误。
- 这类错误直接查看错误提示信息(双击),解决问题,或者凭借经验就可以搞定。
2. 链接型错误
- 一般为未包含头文件、符号名(函数名、变量名)不存在或者拼写错误。
- 看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。
3. 运行时错误
- 一般为程序运行起来时发现的错误。
- 只能借助调试定位到问题所在,这种问题最难解决。