• 程序调试技巧


    在这里插入图片描述

    本期介绍🍖
    主要介绍:什么是调试,调试的快捷键有哪些,以及这些快捷键的功能,调试时如何查看相关信息如:临时变量、内存数据、调用堆栈、汇编指令、寄存器。👀



    一、什么是bug🍖

      bug在程序设计中的术语,是指在软件运行中因为程序本身有错误而造成的功能不正常、体验不佳、死机、数据丢失、非正常中断等现象。而历史上第一个bug其实是由一只钻入计算机中的臭虫导致的计算机无法正常运行,这就是我们今天最爱说的 “ bug ” 的由来。它的意思,和原身一致,真就是“一只臭虫”。
    在这里插入图片描述


    二、什么是调试🍖

      调试(debug)又称:除错。是发现和减少计算机程序或电子仪器设备中程序错误的一个过程。那大家是如何调试的呢?是如下图这样的玄学式调试吗,如果是的请扣一。
    在这里插入图片描述
      所以我们要拒绝玄学式调试。做到在开始调试前让自己的头脑清晰,知道程序将会如何执行;然后验证猜想,看程序是否按照我们的想法在执行。如若程序并没有按照想法在走,那就说明程序出错了。


    2.1 调试的基本步骤🍖

    1. 发现程序错误的存在
    2. 以隔离、消除等方法对错误进行定位
    3. 确定错误产生的原因
    4. 提出纠正错误的解决方案
    5. 对错误给予改正,并重新调试


    2.2 Debug和Release版本🍖

      Debug版本:通常称为调试版本,包含调试信息且不做任何优化,所以代码存放空间较大,但便于程序员调试程序。
      Release版本:称为发布版本用户版本,它往往进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户更好的使用。

    在这里插入图片描述
      注意:Release版本是无法进行调试的,我们所说的调试是在Debug版中进行的。


    三、调试快捷键🍖

    • F5:启动调试,通常与F9一起联用,使得程序可以直接跳到下一个断点处,若没有设置断点则会执行完整个程序才停下来。
    • F9:设置 / 删除断点,可以在程序的任意位置设置断点,这样就可以使得程序在想要的位置随意停止,然后通过F10一步一步细致的执行下去。
    • F10:逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,也可以是一条语句。
    • F11:逐语句,每次只执行一条语句,可以使得我们的执行逻辑进入函数的内部。
    • shift + F5:停止调试。
    • ctrl + F5:开始执行(不调试)。
    • ctrl + shift + F5:重启调试。
    • ctrl + F9:启用 / 禁用断点。
    • ctrl + shift + F5:删除全部断点。

      条件断点:其实我们可以给断点赋上一个条件,只有在该断点处且满足该条件时F5才会停下来。如何设置呢?如下图所示:
    在这里插入图片描述

      注意1:F10和F11的区别是体现在函数调用时的,F10会忽略调用函数时的细节,而F11则会进入被调函数内部观察细节。


    四、调试时查看程序当前信息🍖

    在这里插入图片描述

      注意:只有当开始调试,窗口才会能够查看相关的信息。


    4.1 查看临时变量的值🍖

      VS编译器中存在三种可以查看临时变量的工具:自动窗口局部变量监视。其中自动窗口和局部变量会通过上下文环境自动显示变量信息,但当执行进入被调函数时,自动窗口和局部变量只会显示函数内部的变量信息,而函数外部信息将不会显示,这就不便与全局的观测。当然监视可以做到上点,但所需观测的变量需要自己添加。

    在这里插入图片描述
      注意:其实存在4个监视,但没什么区别,平常使用只需要打开一个即可。


    4.2 查看内存信息🍖

      我们可以通过以下这种方法来查看内存中存放的数据,或者通过地址观测变量在内存中是如何存放的。
    在这里插入图片描述


    4.3 查看调用堆栈🍖

      我们知道一个复杂的代码中函数的调用关系时极其混乱的,这是就可以通过调用堆栈,清晰的反应函数的调用关系以及当前调用所处的位置。
    在这里插入图片描述


    4.4 查看汇编信息🍖

      通过该操作可以查看源代码编译完之后所对应的汇编代码。
    在这里插入图片描述


    4.5 查看寄存器信息🍖

    在这里插入图片描述


    五、调试实例🍖

      下面我会给出两个例子来让大家自己调试找错。

    例1:为n输入一个值,请求出 1!+2!+3!+……+n!最后结果。🍖

    #include
    
    int main()
    {
    	int n = 0;
    	scanf("%d", &n);
    	int i = 0;
    	int del = 1;
    	int sum = 0;
    	for (i = 1; i <= n; i++)
    	{
    		int j = 0;
    		for (j = 1; j <= i; j++)
    		{
    			del *= j;
    		}
    		sum += del;
    	}
    	printf("%d ", sum);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    例2:奇妙的死循环(找错,是什么导致死循环的出现)🍖

    #include
    
    int main()
    {
    	int i = 0;
    	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    	for (i = 0; i <= 12; i++)
    	{
    		arr[i] = 0;
    		printf("hehe\n");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    解答时间🍖

      通过调试很容易就能过找到例1中的错误,是由于程序在实现阶乘的时候不断地积增导致的错误,想要改正只需要在求下一数的阶乘时重置累积器就行了。

      至于例2的情况就较为复杂了,首先对其进行调试
    在这里插入图片描述

      我想看到这很多小伙伴都会疑惑:为什么变量i和arr[12]所处在同一块空间上?这就要讲到代码的底层逻辑了,首先我们需要知道三个知识点:

    1. 栈区可以用来存放局部变量函数的形参函数调用所开辟的空间
    2. 栈区的使用规则是:先使用高地址,再使用低地址
    3. 数组在内存中存放的形式是:数组随着下标的增长,地址是由低到高变化的

      更具上面这三条规则,我们就可以绘制一张关于例2的底层原理图,通过该图就不难看出到底是为什么会导致死循环的出现。
    在这里插入图片描述


    六、编程常见错误

    在这里插入图片描述

    1. 编译型错误(语法错误)——常在编译期间被检查出来
      在这里插入图片描述
        语法错误可以直接看错误提示信息(双击),解决问题或者凭借经验就可以搞定,相对来说简单。

    2. 链接错误(出现在链接期间的错误)
      在这里插入图片描述
        链接型错误大部分原因是:找不到函数的符号名(1.标识符不存在2.标识符拼写错了)。链接型错误无法通过双击(提示信息)找到问题出发地,但可以使用搜索快捷键(ctrl + f) 搜索提示信息中的函数名来找到出错位置。

    3. 运行时错误
        可能会导致代码崩溃、死循环、数据泄露等等一系列问题的出现,甚至有时还会出现幽灵bug,令人防不胜防。而解决的方法就是借助调试,逐步定位问题,再解决。


    在这里插入图片描述

    这份博客👍如果对你有帮助,给博主一个免费的点赞以示鼓励欢迎各位🔎点赞👍评论收藏⭐️,谢谢!!!
    如果有什么疑问或不同的见解,欢迎评论区留言欧👀。

  • 相关阅读:
    计算机网络---万维网(WWW)
    登录功能测试点
    SQL实现模糊查询的四种方法总结
    【泛微ecology】ecology的补丁包升级
    【SQL解析】- SQL血缘分析实现篇01
    【Linux从入门到精通】通信 | 管道通信(匿名管道 & 命名管道)
    2022 计网复习计算题【太原理工大学】
    详细讲解面试的 IO多路复用,select,poll,epoll
    Two-Stream Consensus Network论文阅读
    C++函数重载及引用
  • 原文地址:https://blog.csdn.net/m0_66769266/article/details/126283835