C语言系列文章 | 调试技巧

目前主要分为三个专栏,后续还会添加:

        专栏如下:                 C语言刷题解析       C语言系列文章       我的成长经历

感谢阅读!

初来乍到,如有错误请指出,感谢!


目录

 一、 什么是bug

 二、什么是调试(debug)?

三、Debug和Release

四、 VS调试快捷键

4.1  环境准备

4.2 调试快捷键

五、监视和内存观察

5.1 监视

5.2 内存  

六、调试举例

七、编程常见错误归类

7.1 编译型错误

7.2 链接型错误

7.3 运行时错误

八、如何写出好的代码


 一、 什么是bug

bug本意是“昆虫”或“虫子”,现在⼀般是指在电脑系统或程序中,隐藏着的⼀些未被发现的缺陷或
问题,简称程序漏洞。
“Bug” 的创始⼈格蕾丝·赫柏(Grace Murray Hopper),她是⼀位为美国海军⼯作的电脑专家,
1947年9⽉9⽇,格蕾丝·赫柏对Harvard Mark II设置好17000个继电器进⾏编程后,技术⼈员正在进⾏整机运⾏时,它突然停⽌了⼯作。
于是他们爬上去找原因,发现这台巨⼤的计算机内部⼀组继电器的触点之间有⼀只⻜蛾,这显然是由于⻜蛾受光和热的吸引,⻜到了触点上,然后被⾼电压击死。
所以在报告中,赫柏⽤胶条贴上⻜蛾,并把“bug”来表⽰“⼀个在电脑程序⾥的错误”,“Bug”这个说
法⼀直沿⽤到今天。

历史上的第⼀个bug(图⽚来⾃⽹络) 


 二、什么是调试(debug)?

当我们发现程序中存在的问题的时候,那下⼀步就是找到问题,并修复问题。
这个找问题的过程叫称为调试,英⽂叫debug(消灭bug)的意思。
        调试⼀个程序,⾸先是承认出现了问题,然后通过各种⼿段去定位问题的位置,可能是逐过程的调
试,也可能是隔离和屏蔽代码的⽅式,找到问题所的位置,然后确定错误产⽣的原因,再修复代码,重新测试。

三、Debug和Release

在VS上编写代码的时候,就能看到有 debug release 两个选项,分别是什么意思呢?

        Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序;

        程序员在写代码的时候,需要经常性的调试代码,就将这⾥设置为 debug ,这样编译产⽣的是 debug 版本的可执⾏程序,其中包含调试信息,是可以直接调试的。
        Release 称为发布版本,它往往是进⾏了各种优化,使得程序在代码⼤⼩和运⾏速度上都是最优的, 以便⽤⼾很好地使⽤。
        当程序员写完代码,测试再对程序进行测试,直到程序的质量符合交付给用户使⽤的标准,这个时候就会设置为 release ,编译产⽣的就是 release 版本的可执⾏程序,这个 版本是用户使⽤的,⽆需包含调试信息等。

release版本和debug版本的对⽐  

        对⽐可以看到从同⼀段代码,编译⽣成的可执行文件的大小,release版本明显要小,而debug版本明显⼤。

四、 VS调试快捷键

         那程序员怎么调试代码呢?

4.1  环境准备

        ⾸先是环境的准备,需要⼀个⽀持调试的开发环境,我们使⽤VS,应该把VS上设置为Debug,如图:

4.2 调试快捷键

调试最常使⽤的⼏个快捷键:
        F9:创建断点和取消断点
        断点的作⽤是可以在程序的任意位置设置断点
        打上断点就可以使得程序执⾏到想要的位置暂定执行,接下来我们就可以使⽤F10,F11这些快捷键,观察代码的执行细节。
        条件断点:满⾜这个条件,才触发断点
        F5:启动调试,经常⽤来直接跳到下⼀个断点处,⼀般是 和F9配合使用。
        F10:逐过程,通常⽤来处理⼀个过程,⼀个过程可以是⼀次函数调用,或者是⼀条语句。
        F11:逐语句,就是每次都执⾏⼀条语句,但是这个快捷键可以使我们的执⾏逻辑进⼊函数内部。
        在函数调用的地⽅,想进⼊函数观察细节,必须使⽤F11,如果使⽤F10,直接完成函数调用。
        CTRL + F5:开始执⾏不调试,如果你想让程序直接运⾏起来⽽不调试就可以直接使用。
VS更多快捷键了解:

五、监视和内存观察

在调试的过程中我们,如果要观察代码执行过程中,上下⽂环境中的变量的值,有哪些方法呢?
这些观察的前提条件⼀定是开始调试后观察,比如:
#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int num = 100;
	char c = 'w';
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		arr[i] = i;
	}
	return 0;
}

5.1 监视

  • 开始调试后,在菜单栏中【调试】->【窗⼝】->【监视】
  • 打开任意⼀个监视窗⼝,输⼊想要观察的对象就行。
  • 打开监视窗口:

在监视窗⼝中观察:

5.2 内存  

如果监视窗⼝看的不够仔细,也是可以观察变量在内存中的存储情况
还是在【调试】->【窗⼝】-> 【内存】
打开内存窗⼝:

        在内存窗口中观察数据:

内存窗⼝怎么看  

 内存窗⼝设置列

        在打开内存窗⼝后,要在地址栏输⼊:arr,&num,&c,这类地址,就能观察到该地址处的数据。
调试内存窗⼝演示
除此之外,在调试的窗⼝中还有:自动窗⼝,局部变量,反汇编、寄存器等窗⼝,自行验证使⽤⼀
下。

六、调试举例

        在VS2022、X86、Debug 的环境下,编译器不做任何优化的话,下⾯代码执⾏的结果是啥?
#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;
}
        程序运⾏,死循环了,调试看看为什么 
        调试可以上⾯程序的内存布局如下:

1. 栈区内存的使⽤习惯是从⾼地址向低地址使⽤的,所以变量 i 的地址是较大的。
        arr数组的地址整体是小于 i 的地址。
2. 数组在内存 中的存放是:随着下标 的增⻓,地址是由低到⾼变化的。
        所以根据代码,就能理解为什么是左边的代码布局了。
        如果是左边的内存布局,那随着数组下标的增⻓,往后越界就有可能覆盖到 i,这样就可能造成死循环的。
这⾥肯定有疑问:为什么i和arr数组之间恰好空出来2个整型的空间呢?
这⾥确实是巧合,在不同的编译器下可能中间的空出的空间大小是不⼀样的,代码中这些变量内存的分配和地址分配是编译器指定的,所以的 不同的编译器之间就有差异了。
所以这个题目是和环境相关的。
从这个理解我们能够体会到调试的重要性,只有调试才能观察到程序内部执⾏的细节,就像医⽣给病人做B超,CT⼀样。

七、编程常见错误归类

7.1 编译型错误

编译型错误⼀般都是语法错误,这类错误⼀般看错误信息就能找到⼀些蛛丝⻢迹的,双击错误信息也能初步的跳转到代码错误的地⽅或者附近。
编译错误,随着语⾔的熟练掌握,会越来越少,也容易解决。

7.2 链接型错误

看错误提⽰信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。
⼀般是因为:
  • 标识符名不存在
  • 拼写错误
  • 头⽂件没包含
  • 引⽤的库不存在

7.3 运行时错误

运⾏时错误,是千变万化的,需要借助调试,逐步定位问题,调试解决的是运⾏时问题。
调试过程中,要做到⼼中有数,也就是程序员自己心里要清晰的知道希望代码怎么执⾏,然后再去看代码有没有按照我们预定的路线在执行。
调试是需要反复去动⼿练习的,调试是可以增加程序员对代码的理解和掌控的,掌握了调试的能力,就能看到本质,就像能给程序做B超⼀样,对程序内部⼀览⽆余。

八、如何写出好的代码

  • 代码运行正常、bug很少、效率高、可读性高、可维护性高、注释清晰、文档齐全
  • 使用assert、尽量使用const、养成良好的编码风格、添加必要的注释、避免编码的陷阱
最后,我想跟大家说程序遇到bug不要害怕,勇敢的去面对它吧,加油奥里给!

  

  • 30
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值