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

2.调试是什么?有多重要?
一名优秀的程序员是一名出色的侦探。
2.1我们是如何写代码的?

2.2又是如何排查出现的问题的呢?
拒绝-迷信式调试!!!!
2.3 调试是什么?
2.4调试的基本步骤
- 发现程序错误的存在
- 以隔离、消除等方式对错误进行定位
- 确定错误产生的原因
- 提出纠正错误的解决办法
- 对程序错误予以改正,重新测试
2.5Debug和Release的介绍
代码示例:
#include <stdio.h>
int main()
{
    char *p = "hello world!";
    printf("%s\n", p);
    return 0;
}上述代码在Debug环境的结果展示:

上述代码在Release环境的结果展示:

Debug和Release反汇编展示对比:

 
 所以我们说调试就是在Debug版本的环境中,找代码中潜伏的问题的一个过程。
 那编译器进行了哪些优化呢?
 请看如下代码:
#include <stdio.h>
int main()
{
    int i = 0;
    int arr[10] = {0};
    for(i=0; i<=12; i++)
    {
        arr[i] = 0;
        printf("hello\n");
    }
    return 0;
}
变量在内存中开辟的顺序发生了变化,影响到了程序执行的结果。
3.Windows环境调试介绍
3.1 调试环境的准备

3.2学会快捷键

最常使用的几个快捷键:
F5
F9
通常情况下F5和F9是连在一起使用的
假如我们这里有个循环,我想让他在循环5次的时候停下来该怎么办呢?
- 第一步设置断点,然后设置条件
- 第二步按下 F5 程序就可以跳到我们指定的地方了



F10
F11
F10 这个快捷键通常是进入函数时使用的也是一个常用快捷键,而 F11 逐语句调试。这个可以进入函数之后搭配使用!

CTRL + F5

更多快捷键点这里!
3.3 调试的时候查看程序当前信息
3.3.1 查看临时变量的值

3.3.2 查看内存信息

3.3.3 查看调用堆栈
3.3.4查看汇编信息
在调试开始之后,有两种方式转到汇编:
 (1)第一种方式:右击鼠标,选择【转到反汇编】:

(2)第二种方式:

可以切换到汇编代码。
3.3.5 查看寄存器信息
4、多多动手,尝试调试,才能有进步。
5、一些调试的实例
5.1实例一
实现代码:求 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
代码结果:

实际输出的是15。
why?
 这里我们就得找我们问题。
首先我们可以确定是结果出现的问题,而结果又是循环来计算,我们就可以进去循环然后把监视窗口调开看看是不是这里的问题?
- 3的阶乘应该是 (1*1)+(1*2)+(1*2*3) =9

这里我们通过调试就可以看到,我们的循环在第三次的时候就出现错误了。本来 ret 里面存储的是 3 的阶乘 9 可结果却是 12
- 这里可以得出我们程序的错误了
- ret这个变量未初始化导致结果与预想不同
代码改进:

5.2 实例二
#include <stdio.h>
int main()
{
	int i = 0;
	int arr[10] = { 0 };
	for (i = 0; i <= 12; i++)
	{
		arr[i] = 0;
		printf("hello\n");
	}
	return 0;
}代码结果:

深度解析为什么会死循环
这时就要用到我们调试里面的内存功能了,其实这里 笔试题 为什么死循环的原因很简单,我们前面说过临时变量是存在 栈区 的,而 栈区 的使用习惯是;
- 先使用高地址的空间在使用地地址的空间
- 所以变量 i就在数组上面一点创建的
- 那么我们就可以画这样一幅图

观察内存
我们这里就可以调试一下,观察临时变量 i 和临时变量 arr 是不是在栈区是这样存储的!
 这里为了观察方便就把数组初始化的时候先放上值!
- 取地址 i 得到的地址是 0x00A6FBC0
- 取地址arr 得到的地址是 0x00A6FBF0

这里就可以看到我们临时变量 arr 和 i 在内存 栈区中地区是这样存储的!当我们循环第13次时 arr[13] 越界!
- 就会找到我们临时变量 i的地址值处从而
- 改为 0开始无限循环

总结:所以我们在使用数组时一定要注意数组越界这个问题,不然就会引发bug。
6.如何写出好(易于调试)的代码
6. 1 优秀的代码:
常见的coding技巧:
6.2 示范:
/***
*char *strcpy(dst, src) - copy one string over another
*
*Purpose:
*    Copies the string src into the spot specified by
*    dest; assumes enough room.
*
*Entry:
*    char * dst - string over which "src" is to be copied
*    const char * src - string to be copied over "dst"
*
*Exit:
*    The address of "dst"
*
*Exceptions:
*******************************************************************************/
char * strcpy(char * dst, const char * src)
{
    char * cp = dst;
    assert(dst && src);
    while( *cp++ = *src++ )
       ;   /* Copy src over dst */
    return( dst );
}注意:
3.3const的作用:
#include <stdio.h>
//代码1
void test1()
{
	int n = 10;
	int m = 20;
	int* p = &n;
	*p = 20;//ok?
	p = &m; //ok?
}
void test2()
{
	//代码2
	int n = 10;
	int m = 20;
	const int* p = &n;
	*p = 20;//ok?
	p = &m; //ok?
}
void test3()
{
	int n = 10;
	int m = 20;
	int* const p = &n;
	*p = 20; //ok?
	p = &m;  //ok?
}
int main()
{
	//测试无cosnt的
	test1();
	//测试const放在*的左边
	test2();
	//测试const放在*的右边
	test3();
	return 0;
}
结论:
const的具体介绍会在之后发布文章
7.编程常见的错误
 7.1 编译型错误
 
 
7.2 链接型错误
7.3 运行时错误
讲解重点:
 介绍每种错误怎么产生,出现之后如何解决。
Visual Studio 2022 你必须知道的实用调试技巧到这里就讲完了,后续我会发布一些其他知识,大家可以订阅一下专栏呀!














