C语言初阶——实用调试技巧

简介: 调试是我们每个程序员都必备的技能之一,调试能在程序逐步运行过程中锁定目标变量,找出问题,解决问题。我们一般把程序中的运行问题称为 "Bug" ,Bug是程序员一生之敌;正所谓一物降一物,面对Bug,我们有调试,只要调试玩的溜,Bug就无所遁形。👾👾👾

🔒前言


 调试是我们每个程序员都必备的技能之一,调试能在程序逐步运行过程中锁定目标变量,找出问题,解决问题。我们一般把程序中的运行问题称为 "Bug" ,Bug是程序员一生之敌;正所谓一物降一物,面对Bug,我们有调试,只要调试玩的溜,Bug就无所遁形。👾👾👾

5957a7c6998c7f0691eca1ffb3e399a.png

94932f5c9a27a2322428dfecd8a64ba.png

🔐正文


我们的调试环境:VS2019,当然VS系列方法都大同小异,掌握核心方法就行了。


🗝️Release与Debug


Release 为发布版本,用户使用的程序就是这个版本;Debug 则是调试版本,是我们程序员使用的开发版本,两者的最大区别:是否对代码进行了优化。

3def2feae8c9dd28b1605fa458d5728.png

fab5646eede7e2d59a90675a901b6a8.png


Debug版本最大优势就是能随便调试,各项开发功能功能齐全,因此找Bug都是在这个版本中进行的。Release版本会对代码进行优化,因此某些开发版本中的Bug可能不会在发布版本中复现,测试人员用的是Release版本,大概率是不会让用户找Bug的,除非某UI。

a73c9ea8efd66544ab9fc65119c3d8a.png

554b5758d300a2c58dd82be5e642abb.png

🗝️常用快捷键


我们的键盘最顶部有一排 Fx 键,这些都是辅助功能键,因为用户用的少,所以大多数厂商都会把这些辅助功能键映射成不同的功能(大多数笔记本自带键盘),比如音量+ - 亮度调节等。而我们开发人员需要用到这些辅助键,因此首先我们要把这些映射状态解除,不同品牌解除方法可能略有差异,联想拯救者的解除方法是 Fn+Esc,这样我们就能愉快的在VS中使用快捷键了。


🔑Ctrl+F5   开始执行不调试


这个快捷键的作用是直接开始执行程序,不进入调试模式,一般用于验证程序的执行情况。

33e0bedbee6e105a909996991cf2080.png



🔑F5   进入调试模式


F5 一般和 F9断点搭配使用,当断点创建后,按F5就可以直接跳到断点处,当然直接使用 F5也是可以进入我们的调试模式的。如果程序没有输入环节,我们的 F5调试会直接执行完程序的。

🔑F9   创建断点


F9 作用就是创建断点,一般是和F5 配合使用,比如我们的程序有30行(假设没有输入语句),但我们想查看20行的变量情况,只需要在20行创建一个断点,然后再按F5 就可以了,程序执行到第20行就会自动停止。当然断点也能用来跳过无用的循环,只需要在创建的断点上右击断点添加条件即可,提高调试效率。

7bfed28a017a18c56f9f169ed3fad1a.png

ce6ae1ed62679e0fa4d5d2ac5cd07c2.png

创建断点的方法(两种):

1.直接鼠标左击想要停止语句所对应的左侧灰色区域。

52b47dc5d55b5223d13b5c7e5b6e227.png

2.鼠标点击目标语句,确保处于选定状态,按F9即可创建断点。

60b07ca830f542eb9c66e7794b05b40.png


🔑F10   逐过程调试(不会进入函数内部)


F10 的作用是可以一步步的进行调试,在调试状态下,按一下F10,程序就走一步,如此重复。因为F5 进入的调试模式,如果没有输入语句或断点截停,程序可能会直接执行完,不利于调试观察,因此我们一般使用F10 进入调试模式,一步一步的走,F5 还得搭配 F9使用。如果遇到函数的话,按F10 并不会进入到函数内部,而是直接出结果,因此如果我们想要实现真正的逐语句调试,还得看F11(后面会介绍的)。

8b4ac4ce399b8fa1d84124f36faf806.png

94a791b02f227e1b7afa0d003bd5d6d.png

94a791b02f227e1b7afa0d003bd5d6d.png


🔑F11 逐语句调试(会进入函数内部)


F11 才是真正意义上的逐步调试,使用F11 可以很仔细的观察到程序的走向,同时F11 能进入函数内部(函数是C语言程序的重要组成部分),因此我们一般使用F10 唤起调试模式,F11 进行调试

0fa68a9a77ba70fd4984384f49105b3.png

9dd67564544e4de10984843fc8d365f.png

🔑小结


以上便是我们在调试时常用的几个快捷键,灵活使用就能很好的进行调试找错。F5 一般是在有断点的情况下使用,而断点是用来跳过无用语句或循环的,F10 是在不想进入函数时使用,F11 则是非常详细的进行调试,会进入函数内部。推荐使用F10 或F11 进入调试模式,而不是F5,避免程序一闪而过。当然VS中还有很多快捷键,比如Ctrl+k+c 注释代码,Ctrl+k+u  取消注释,这里推荐一篇博客,里面介绍了很多快捷键:VS中常用的快捷键_MrLisky的博客-CSDN博客_vs快捷键


🗝️调试中的各种信息


现在我们已经能进入调试并使用调试了,那么调试中的各种窗口信息又该怎么看呢?


下面让我给大家简单介绍几个常用窗口:


🔑监视窗口


这个窗口主要是用来监视各种变量、表达式的值变化的,也是最常用的窗口之一。

3de4377222f01bd32e7aa22ddf9877e.png

583be6b0a4d3b94b91649477e61bf1c.png


当我们设好监视值后,就可以按F10 或F11 来观察变量的变化情况,当然监视窗口内的监视值可以随时改变,也可以在调试过程中唤出监视窗口,使用都很自由。

🔑自动窗口与局部变量


这两个窗口就是监视窗口的改版,偏向于半自动化,但是不如监视窗口自由


1f64e6e3954ad09408702d778677272.png

🔑内存窗口


内存窗口主要是用来查看变量在内存中的地址信息的,对于指针的纠错需要时刻观察指向地址。

652826b72345bc096837695f8b8ebef.png


0f2fb41eaf05d4060492d62fe390c93.png

🔑反汇编窗口


汇编代码出现于高级语言之前,这种夹杂着机器语言和汇编指令组成的代码比较难懂,但电脑能快速读懂,运行速度是极快的。

67df8740a92002ab0324edb7bcfd600.png

ebf58a1d26d741623ffca01e591fbb3.png

🔑寄存器


寄存器是指有限存贮容量的高速存贮部件,寄存器位于CPU上,通常用来存储反复使用的数据


3f797d3b3c4c2d4fefb04ce8487b87a.png


其实还有很多窗口,比如调用堆栈、线程、进程等,但目前还用不上,因此没有做介绍。


🗝️调试实例


现在我们已经对调试有一定的了解了,话不多说,直接把问题程序拿出来调试一下!


代码如下:

e7a34fe652c797db185a21798120b77.png

猜猜运行结果是什么?栈溢出,报错?还是直接运行失败?

答案都不是,是死循环!

7b671a0a4fc278e9cffc90ea3bf4886.png

当然这个死循环是需要条件的:1.在VS内  2.x86环境下  3.先定义i,后定义arr  

081cc7f4782ab9194e5fd1494b0e827.png

//调试示例
int main()
{
  int i = 0;
  int arr[10] = { 0 };
  for (i = 0; i <= 12; i++)
  {
    arr[i] = 0;
    printf("%d\n", i);
  }
  return 0;
}

像这种隐性的Bug不通过调试是根本无法发现的,只有通过调试分析,我们才能找出问题。


🗝️优秀编码技巧


我们只有养成良好的编码风格,才有可能从源头上避免Bug的产生,比如:


1.代码能正常运行

2.Bug较少

3.三高:代码执行效率高、可读性高、可维护性高

4.注释要言简意赅

5.文档要齐全

下面展示一段编码风格十分优秀的代码:

//模拟实现strcpy
#include<stdio.h>
#include<assert.h>
char* my_strcmp(char* pa1, const char* pa2)
{
  assert(pa1 && pa2);//断言防止空指针
  char* str = pa1;//纪录arr1的首地址
  while (*pa1++ = *pa2++)
  {
    ;
  }
  return str;
}
int main()
{
  char arr1[20] = "xxxxxxxxxxx";
  char arr2[] = "Hello Coder!";
  printf("%s\n", my_strcmp(arr1, arr2));
  return 0;
}



在上面这段代码中,模拟实现了strcpy字符串拷贝函数,涉及到的知识有:链式访问、const保护被拷贝的指针,assert断言、利用 '\0' 巧妙赋值和结束循环、完善函数的返回类型。通过这几点优化,使得我们的代码变动更加安全、效率也更高,安全就意味着不容易出现Bug,算得上是一段非常漂亮的代码。


优秀的风格见过了,下面就来看看有哪些编程技巧吧!


1.使用asser断言

2.尽量使用const保护不想被修改的变量

3.养成良好的编码风格

4.添加适当的注释

5.避免编程期间出现的陷阱

🗝️编程常见错误


我们在编写程序时常常会犯很多错误,根据严重等级,大概可以分为一下三种错误:


🔑1.编译型错误


编译型错误很容易发现,属于犯错编译器立马就能给出提示的那种,比如逻辑错误(判断式写错、悬空else)、关键字拼写错误等,这种错误编译器能够辅助定位错误,很简单,修改解决错误即可。


🔑2.链接型错误


链接型错误一般指自定义函数拼写错误或者标识名未定义,这种错误需要结合上下文寻找,用点心仔细寻找也能发现问题,当然编译器肯定也会辅助定位错误位置。


🔑3.运行中错误


这种错误就比较棘手了,程序能运行,说明语法没有问题,问题出在语句的逻辑搭配上面,比如计算1+1=3,此时需要进入调试模式,逐步寻找,利用本文的知识,就能解决问题!

🔓总结


 调试的内容并不多,无非就是几个快捷键和几个信息窗口搭配使用,解决问题最关键的还是经验,只有调试的多了,我们才能积累到这些失败经验,牢记于心,不断升级,成为一名调试大师!


 调试就像破案的过程,我们就像侦探,而Bug就是我们的目标,通过蛛丝马迹,解决Bug!🏄‍♂️


 如果你觉得本文写的还不错的话,期待留下一个小小的赞👍,你的支持是我分享的最大动力!


 如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正!

b69ecf6ea37310ba075db932210d181.png



目录
相关文章
|
1月前
|
存储 编译器 C语言
【C语言】VS实⽤调试技巧&(Debug和Release)监视&内存2
【C语言】VS实⽤调试技巧&(Debug和Release)监视&内存
|
1月前
|
程序员 C语言 C++
【C语言】VS实⽤调试技巧&(Debug和Release)监视&内存1
【C语言】VS实⽤调试技巧&(Debug和Release)监视&内存
|
1月前
|
C语言
C语言使用宏定义实现等级调试输出PRINT_LEVEL
C语言使用宏定义实现等级调试输出PRINT_LEVEL
|
4天前
|
NoSQL 编译器 C语言
【C 言专栏】C 语言中的调试技巧与工具
【5月更文挑战第6天】在C语言编程中,调试是必不可少的技能,涉及基本技巧如打印输出、断点调试和单步执行,以及使用GCC、GDB、Visual Studio和Eclipse CDT等工具。高级技巧包括内存检查和性能分析。通过分析问题、设置断点、逐步调试和检查逻辑来解决错误。调试时需保持耐心,合理选用工具,记录过程,并避免过度调试。熟练掌握这些技能将提升代码质量和开发效率。
【C 言专栏】C 语言中的调试技巧与工具
|
4月前
|
NoSQL Linux Redis
redis源码调试---vscode使用技巧-----C语言跳转到函数定义
redis源码调试---vscode使用技巧-----C语言跳转到函数定义
73 0
|
2月前
|
程序员 编译器 C语言
编程最重要的技术之一 — 调试(以C语言代码为例)
编程最重要的技术之一 — 调试(以C语言代码为例)
42 0
|
2月前
|
存储 程序员 编译器
C语言第十三弹---VS使用调试技巧
C语言第十三弹---VS使用调试技巧
|
2月前
|
存储 程序员 编译器
c语言从入门到实战——VS2022实用调试技巧
Visual Studio是集成开发环境,其内置了多种调试工具和技巧帮助开发人员在开发过程中解决问题。以下是一些VS实用调试技巧的简介
53 0
|
2月前
|
C语言
错误的集合(初阶C语言刷题)
错误的集合(初阶C语言刷题)
|
2月前
|
C语言
C语言:初阶测试错题(查漏补缺)
C语言:初阶测试错题(查漏补缺)
21 0