C语言调试技巧

发布于:2022-12-19 ⋅ 阅读:(206) ⋅ 点赞:(0)

一. 什么是bug?

bug 其实在一开始的英文中是小昆虫的意思

在1947年9月9日,葛丽丝·霍普(Grace Hopper)发现了第一个电脑上的bug。有一次Mark II突然宕机,整个团队都搞不清楚为什么电脑不能正常运作了。经过大家的深度挖掘,发现原来是一只飞蛾意外飞入了一台电脑内部而引起了故障(如图所示)。这个团队很快排除错误,并在日志本中记录下了这一事件。也因此,人们逐渐开始用“Bug”(原意为“虫子”)来称呼计算机中的隐错。现在在华盛顿的美国国家历史博物馆中还可以看到这个遗稿。

二. 什么是调试?

1. 调试的定义

修补、改正软件程序错误的过程被称为调试。

2. 调试的基本步骤

发现程序错误的存在。
以隔离、消除的方式对错误进行定位。
确定错误产生的原因。
提出纠正错误的解决办法。
对程序错误予以改正或重构,重新测试。

比如说以下面这段代码为例

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	// 要求 求1!+2!+3!+...+n!
	int i, j;
	int sum = 0;
	int ret = 1;
	int n = 0;
	scanf("%d", &n);
	for ( i = 1; i <= n; i++)
	{
		for ( j = 0; j < i; j++)
		{
			ret *= j;
		}
		sum += ret;
	}

	printf("%d\n", sum);
	return 0;
}

可是运行的结果却是下面这样子

在这里插入图片描述

明显与我们的预期不符啊

我们便开始调试程序

经过调试窗口我们发现

原来问题出现在这里

在这里插入图片描述
j一开始出现的数字是0

0乘上任何数都是0 最后的sum当然会是0啦

那我们将代码修改如下

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	// 要求 求1!+2!+3!+...+n!
	int i, j;
	int sum = 0;
	int ret = 1;
	int n = 0;
	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;
}

再运行试试

在这里插入图片描述

奇怪了 1!+2!+3!并不等于十五啊

这说明我们的程序又出现了问题

我们继续开始调试

在这里插入图片描述

那经过我们新一轮的debug 我们又发现了 原来啊ret的值再每次循环后没有重置 最终导致了这个错误

那么我们再每次进入循环后将ret的值重置为1

代码如下

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	// 要求 求1!+2!+3!+...+n!
	int i, j;
	int sum = 0;
	int ret = 1;
	int n = 0;
	scanf("%d", &n);
	for ( i = 1; i <= n; i++)
	{
		ret = 1;
		for ( j = 1; j <= i; j++)
		{
			
			ret *= j;
		}
		sum += ret;
	}

	printf("%d\n", sum);
	return 0;
}

运行结果如下

在这里插入图片描述

这个时候我们可以发现 代码就可以无bug完美运行啦

这就是一个完成的debug过程

3. Debug版本和Release版本

Debug版本就是我们程序员使用的调式版本 它包含调试信息 且不做任何优化 便于程序员调试

Release版本就是一个发布版本 它会对程序做各种优化 使得程序在大小和运行速度上是最优的 以便于用户更好的使用

Release版本
在这里插入图片描述
Debug版本

在这里插入图片描述

我们可以很明显的发现两个程序的大小都不一样

我们再来看以下的代码

int main()
{
	int i = 0;
	int arr[10] = { 0 };
	for ( i = 0; i <= 12; i++)
	{
		arr[i] = 0;
		printf("hehe\n");
	}
	return 0;
}

在release版本下
在这里插入图片描述
它没有报错 打印了13个 hehe

我们再回到debug版本下

在这里插入图片描述

我们惊奇的发现 它竟然进入了死循环

它们之间又什么区别呢?

这就是因为两个版本之间的优化不同所导致的

至于为什么会在debug x86环境下进入死循环

这里大家要首先知道两个知识点

数组随着下标的增长 地址由低到高不断变化

栈区中优先使用高地址进行压栈操作

下面我画图来为大家讲解一下

在这里插入图片描述

在数组越界访问到12的时候实际上修改了i的值 从而导致了死循环

这里提一嘴

vc6.0编译器上i和arr数组之间相隔的内存为1
vs中为2
gcc中为0

4. 进入调试环境

在这里插入图片描述
设置进入debug环境

5. 记住快捷键

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

F9 创建断点和取消断点 断点的重要作用,可以在程序的任意位置设置断点。
这样就可以使得程序在想要的位置随意停止执行,继而一步步执行下去。

F10 逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句。与F11的区别是它不进入函数内部

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

6. 调试的时候查看变量的值

在这里插入图片描述

我们可以使用shift + f9来查看变量的值

7. 查看一些其他数据

在这里插入图片描述

其他所有数据 包括内存 反汇编 寄存器这些 都可以通过调试 - - 窗口里面查看

以上就是本篇博客的全部内容啦 由于博主才疏学浅 所以难免会出现纰漏 希望大佬们看到错误之后能够

不吝赐教 在评论区或者私信指正 博主一定及时修正

那么大家下期再见咯

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到