贯彻程序员一生的调试

大家好,这里是笨鸟先飞上课学习了一些调试的小技巧在这里分享给大家!

种一棵树最好的时间是十年前,其次是现在

typore

1.0什么是BUG?

1965769-74c7de2de82370b8

历史上第一个被发现的计算机错误,是因为有一只飞蛾堵住了晶体管,这也是第一个计算机错误。

2.0调试是什么?有多重要?

所有发生的事情都一定有迹可循,如果问心无愧,就不需要掩盖也就没有迹象了,如果问心有愧, 就必然需要掩盖,那就一定会有迹象,迹象越多就越容易顺藤而上,这就是推理的途径。

顺着这条途径顺流而下就是犯罪,逆流而上,就是真相。

一名出色的程序员是一名出色的侦探。

每一次调试都是尝试破案的过程。

我们初学者是怎么写代码的?

15726176865208

又是如何排查问题的呢?

1

这是初学者常犯的错误,我们要拒绝迷信式调试。找出问题所在

2.1调试是什么?

调试(英语:Debugging / Debug),又称除错,是发现和减少计算机程序或电子仪器设备中程序

错误的一个过程。

2.2调试的基本步骤
  • 发现程序错误的存在
  • 以隔离,消除等方式去错误进行定位
  • 确定错误产生的原因
  • 提出纠正错误的方法
  • 对程序错误予以改正,重新测试
2.3Debug和Release的介绍。

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

Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优 的,以便用户很好地使用。

image-20220809164727457

我们可以看到在发布版本(Release)的大小是比调试版本(Debug)要小的,因为它已经被优化过了。

image-20220809164954944

我们在调试时一定选择Debug环境,才能进行正常的调试。

3.0调试中快捷键的使用

F5

启动调试,经常用来跳到下一个断点处。

F9

用来创建断点和取消断点

断点的重要作用,可以在程序的任意位置设置断点

这样就可以使得程序在想要的位置随意停止执行,继而一步步执行下去。

F10

逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句。

F11

逐语句,就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部(这是最常用的)。

CTRL+F5

开始执行不调试,如果你想让程序直接运行起来而不调试就可以直接使用。

[(146条消息) VS中常用的快捷键_MrLisky的博客-CSDN博客_vs快捷键](https://blog.csdn.net/mrlisky/article/details/72622009)

3.1断点的使用

image-20220809174419776

在将断点打到13行时,按住F5会直接来到断点处,我们可以看到之前的程序已经全部运行,输出也打印在了屏幕上。断点为我们在调试过程中省去了许多麻烦,在我们知道前面都没有问题,而我们有问题时可以剩去前面调试的部分。

3.2调试时查看当前程序信息
3.2.1查看临时变量的值

在调试开始过后,用于观察变量的值。

image-20220809174938258

3.2.2查看内存信息

在调试开始之后,用于观察内存信息。

image-20220809175430025

3.2.3查看调用堆栈

image-20220809180311337

通过调用堆栈,可以清晰的反应函数的调用关系以及当前调用所处的位置。

3.2.4查看汇编信息

在调试过后,有两种方法转到反汇编。

第一种,调试过后鼠标右键点到反汇编

image-20220809180753175

第二种

image-20220809180928493

4.0调试一定要多多动手才能进步

  • 一定要熟练掌握调试技巧。

  • 初学者可能80%的时间在写代码,20%的时间在调试。但是一个程序员可能20%的时间在写

程序,但是80%的时间在调试。

  • 多多使用快捷键,提升效率。

5.0调试实例

5.1实例一

实现代码求1!+2!+3!+4!…+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++)
	{
		for (j = 1; j <= i; j++)
		{
			ret *= j;
		}
		sum += ret;
	}
	printf("%d\n", sum);
	return 0;

image-20220809182131616

我们这串代码在输入3时,我们期待3的阶乘之和为9,但是实际输出却是15.这就说明我们的代码出现了问题,我们这个时候就需要调试来解决问题。

image-20220809185552110

通过我们分析我们代码逻辑当i=1时,我们求1!,当我们i=3的时候应该求的是3!ret的值应该是6,通过调试我们发现此时ret的值变为了12,我们通过倒推,这个12是6乘以2得来的,那这个2是那来的呢?原来是上个循环当i=2时,ret的值就变为了2。而我们想到这里就知道了改进的方法,每次循环之前将ret的值变为1,不然会一直累积乘。

image-20220809190920379

通过调试我们可以发现问题,并且去解决它。

5.2实例二
int main()
{
	int i = 0;
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	for (i = 0; i <= 12; i++)
	{
		arr[i] = 0;
		printf("hehe\n");
	}
	return 0;
}

此题这是来自一本书《C陷阱与缺陷》,同时在nice公司的笔试题中也考过类似题目,非常的有意思拿出来与大家看看。

我们观察这个程序,第一眼看到的可能是数组越界访问了,明明是下标只有9,却访问到了12。按照我们正常来思考,它可能要输出13个hehe在屏幕上,因为这个程序循环13次嘛。

但其实这个道题的答案是死循环的输出hehe

image-20220809194014097

这是为什么呢?

image-20220809194033936

注意:这里i和数组arr的距离并不是固定两个字节,这取决于我们使用的编译器,万不可死记硬背当作万能公式。

这其实是一个很偶然的现象,但它确实存在。让我们的前辈摔过跟头,我们需要注意一点。

6.0优秀的代码

  1. 代码运行正常
  2. bug很少
  3. 效率高
  4. 可读性高
  5. 可维护性高
  6. 注释清晰
  7. 文档齐全
6.1常见的coding技巧

\1. 使用assert

  1. 尽量使用const
  2. 养成良好的编码风格
  3. 添加必要的注释
  4. 避免编码的陷阱。
6.2模拟实现strcpy

image-20220810141250280

6.3const修饰指针变量

image-20220810140644025

7.0编译常见错误

7.1编译型错误

直接看错误提示信息(双击),解决问题。或者凭借经验就可以搞定。相对来说简单。

image-20220810142617081

7.2链接型错误

看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是标识符名不 存在或者拼写错误。

image-20220810142929540

7.3运行时错误

借助调试,逐步定位问题,最难搞。

温馨提示

做一个有心人,积累排查经验。

20200808071514_wbgrx

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值