程序调试技巧

发布于:2023-01-21 ⋅ 阅读:(365) ⋅ 点赞:(0)

在这里插入图片描述

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



一、什么是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<stdio.h>

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);
}

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

#include<stdio.h>

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的情况就较为复杂了,首先对其进行调试
在这里插入图片描述

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

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

  更具上面这三条规则,我们就可以绘制一张关于例2的底层原理图,通过该图就不难看出到底是为什么会导致死循环的出现。
在这里插入图片描述
  注意:在release版本下执行该程序是不会死循环的。根本原因是由于程序优化所导致的栈区内存开辟方式默认开辟方式(栈区使用规则:先使用高地址,再使用低地址。)之间出现了差异。如下图所示:在release版本下变量i的地址反而会低于arr数组的地址。
在这里插入图片描述


六、编程常见错误

在这里插入图片描述

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

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

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


在这里插入图片描述

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

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

网站公告

今日签到

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