目录
什么是bug?
调试是什么?有多重要?
调试是什么?
调试的基本步骤
Debug和Release的介绍
Windows环境调试介绍
调试环境的准备
学会快捷键
调试的时候查看程序当前信息
查看临时变量的值
查看内存信息
查看调用堆栈
查看汇编信息
查看寄存器信息
一些调试的实例
实例一
实例二
什么是bug?
在计算机编程中,"bug"(中文通常翻译为"错误"或"缺陷")是指程序中存在的错误或问题。
当程序无法按照预期的方式工作时,我们就可以说程序中出现了 bug。
调试是什么?有多重要?
调试是什么?
调试(Debugging)是指在软件开发过程中识别、定位和解决程序中的错误或缺陷的过程。调试是一种通过逐步执行代码、观察和分析程序行为来找出程序中的问题的方法。
在软件开发中,调试是一个非常重要的环节,它允许开发人员检查和修复错误,以确保程序的正确性和稳定性。调试工具通常提供了以下功能来帮助开发人员进行调试:
断点(Breakpoints):在代码中设置断点,程序执行到断点处时会暂停,开发人员可以查看变量值、执行状态等信息。
单步执行(Step Execution):逐行执行程序,观察每一步的结果,以便定位错误。
变量监视(Variable Watch):监视变量的值并及时更新,以便观察变量在不同执行阶段的变化。
栈追踪(Stack Trace):跟踪函数调用的层次和顺序,以便了解程序的执行流程。
日志输出(Logging):将程序运行时的信息输出到日志文件中。
调试的基本步骤
发现程序错误的存在
以隔离、消除等方式对错误进行定位
确定错误产生的原因
提出纠正错误的解决办法
对程序错误予以改正,重新测试
Debug和Release的介绍
Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。
Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优 的,以便用户很好地使用。
需求 ——> 设计和开发 ——> 测试 ——> 产生验收 ——> 发布
Debug 模式:
- 主要用于开发和调试阶段。
- 生成的代码通常包含了调试信息,以便在出现错误时能够更容易地进行调试和定位问题。
- 编译器会进行更多的优化,以提供更好的调试体验。
- 生成的可执行文件通常较大,占用更多的内存。
- 可以使用断言(assertions)来验证代码的正确性。
Release 模式:
- 主要用于最终发布和部署阶段。
- 生成的代码通常经过了更多的优化,以提供更好的性能和较小的文件尺寸。
- 调试信息被剥离,使得代码更难以被逆向工程或调试。
- 通常会关闭断言,以提高执行效率。
- 生成的可执行文件通常较小,占用较少的内存。
总的来说,Debug 模式主要用于开发和调试阶段,提供更好的调试能力和错误定位;而 Release 模式主要用于最终发布和部署阶段,追求更好的性能和较小的文件尺寸。
Windows环境调试介绍
注:linux开发环境调试工具是gdb,后期会介绍
调试环境的准备
在环境中选择debug选项,才能使代码正常调试。
学会快捷键
在 Windows 环境下,调试程序时可以使用一些常用的快捷键来提高效率。以下是一些常见的调试快捷键:
- F5:开始调试(继续执行程序)
- F9:在当前行设置或取消断点
断点的重要作用,可以在程序的任意位置设置断点。 这样就可以使得程序在想要的位置随意停止执行,继而一步步执行下去。
- F10:逐过程执行,跳过函数内部的调用
- F11:逐语句执行,进入函数内部的调用
- Shift + F11:从当前函数返回到调用该函数的地方
- F12:跳转到函数定义或变量声明处
- Ctrl + F5:无调试方式运行程序
- Ctrl + Shift + F5:停止运行程序
- Ctrl + B:打开或关闭断点窗口
- Ctrl + Alt + C:显示/隐藏自动窗口
- Ctrl + Alt + E:显示/隐藏异常窗口
- Ctrl + Alt + T:显示/隐藏线程窗口
- Ctrl + Alt + I:显示/隐藏快速监视窗口
- Ctrl + Alt + D:显示/隐藏内存窗口
- Ctrl + Alt + L:显示/隐藏本地变量窗口
这些快捷键可以根据不同的集成开发环境(IDE)或调试工具进行配置和扩展。
条件断点(Conditional Breakpoint)是在调试过程中设置的一种断点,它允许程序在满足特定条件时暂停执行。当程序执行到带有条件断点的代码行时,会先计算断点条件,如果条件为真,则程序会暂停执行,允许开发者检查和调试代码。
条件断点在调试复杂的程序时非常有用,可以帮助开发者在特定的条件下追踪代码的执行,以便更好地理解程序的行为和问题所在。
在不同的集成开发环境(IDE)或调试器中,设置条件断点的方法可能会有所不同。一般来说,你可以在设置断点的地方右键单击,并选择设置条件或类似的选项。然后,你可以编写一个表达式作为条件,只有当该表达式的值为真时,程序才会在该断点处暂停。
调试的时候查看程序当前信息
查看临时变量的值
在调试开始之后,用于观察变量的值
在调试窗口:
查看内存信息
在调试开始之后,用于观察内存信息。
查看调用堆栈
反应的函数的调用逻辑
调用堆栈(Call Stack)是计算机程序执行过程中用来跟踪函数调用的一种数据结构。它以栈(Stack)的形式组织函数调用的顺序和相关信息。
当一个函数被调用时,该函数的相关信息(如返回地址、局部变量等)会被压入堆栈。当函数执行完毕或遇到返回语句时,相关信息会从堆栈中弹出,程序继续执行上一个函数的代码。
调用堆栈的主要作用是为了管理函数的调用顺序和局部变量的生命周期。它允许程序在函数调用的过程中进行嵌套调用,并能够正确地返回到调用点。
查看汇编信息
查看寄存器信息
在计算机体系结构中,寄存器(register)是位于CPU内部的一种高速存储器,用于存储和处理指令和数据。寄存器在计算机执行指令时起到了至关重要的作用。
不同的计算机体系结构和处理器架构会有不同的寄存器组织和使用方式。以下是一些常见的寄存器类型:
通用寄存器(General Purpose Registers):用于存放操作数和中间结果,在程序执行过程中可以被任意使用。通常用于存储整数类型数据。
- 在x86架构中,常见的通用寄存器有EAX、EBX、ECX、EDX等。
- 在ARM架构中,常见的通用寄存器有R0、R1、R2、R3等。
程序计数器(Program Counter):也称为指令指针(Instruction Pointer),用于存放当前正在执行的指令的地址。
- 在x86架构中,程序计数器通常称为EIP(Extended Instruction Pointer)。
- 在ARM架构中,程序计数器通常称为PC(Program Counter)。
栈指针(Stack Pointer):用于指向栈顶的地址,用于处理函数调用、局部变量等。
- 在x86架构中,栈指针通常称为ESP(Extended Stack Pointer)。
- 在ARM架构中,栈指针通常称为SP(Stack Pointer)。
状态寄存器(Status Registers):用于存放处理器的状态信息,例如条件码、溢出标志、零标志等。
- 在x86架构中,常见的状态寄存器有FLAGS寄存器。
- 在ARM架构中,常见的状态寄存器有CPSR(Current Program Status Register)。
除了上述寄存器外,不同的体系结构还可能包含其他特定功能的寄存器,用于浮点运算、向量计算、特殊功能等。
需要注意的是,具体的寄存器信息和使用方式会根据不同的CPU架构和操作系统而有所差异
一些调试的实例
实例一
实现代码:求1!+2!+3!+……+n! ;不考虑溢出。
int main() {
int i = 0;
int sum = 0;//保存最终结果
int n = 0;
int ret = 1;//保存n的阶乘
scanf("%d", &n);
for(i=1; i<=n; i++)
{
int j = 0;
for(j=1; j<=i; j++)
{
ret *= j;
}
sum += ret;
}
printf("%d\n", sum);
return 0;
}
这时候我们如果3,期待输出9,但实际输出的是15。 why?
这里我们就得找我们问题
最后发现是ret值的问题 (在每次循环前初始化值)
实例二
#include <stdio.h>
int main()
{
int i = 0;
int arr[10] = {0};
for(i=0; i<=12; i++)
{
arr[i] = 0;
printf("hehe\n");
}
return 0;
}
这时候死循环打印hehe