[C]基础6.VS实用调试技巧

  • 博客主页:算法歌者
  • 本篇专栏:[C]
  • 您的支持,是我的创作动力。


0、总结

在这里插入图片描述

1、bug和调试(debug)

bug:一般是指在电脑系统或程序中,隐藏着的一些未被发现的缺陷或问题,简称程序漏洞

debug:找问题的过程称之为调试,英文叫debug,意思是消灭bug。

调试一个程序,首先是承认出现了问题,然后通过各种手段去定位问题的位置。可能是逐过程的调试,也可能是隔离和屏蔽代码的方式,找到问题所在的位置,然后确定错误产生的原因,再修复代码,重新测试。

2、Debug和Release

在这里插入图片描述

在VS上编写代码的时候,就能看到有DebugRelease两个选项,分别是什么意思呢?

Debug:称为调试版本,它包含调试信息,不进行任何优化,便于程序员调试。编写代码时,若需频繁调试,则设置为Debug模式,编译后产生的是可直接调试的Debug版本可执行程序。
在这里插入图片描述

Release:称为发布版本,它经过各种优化,以达到代码大小和运行速度的最佳状态,便于用户使用。程序员完成代码编写并经过充分测试,确保程序质量符合交付标准后,会设置为Release模式进行编译,产生用户使用的Release版本可执行程序,该版本不包含调试信息。
在这里插入图片描述

对比可以看到从同一段代码,编译生成的可执行文件的大小,release版本明显要小,而debug版本明显要大。

3、VS调试快捷键

程序员如何调试代码?

1、首先是环境准备,把VS上设置为Debug,如图:

在这里插入图片描述

2、其次,掌握调试中最常使用的几个快捷键:

  • F9:用于创建或取消断点。断点允许在程序的任意位置暂停执行,便于使用F10、F11等快捷键深入观察代码的执行细节。
  • 条件断点:仅当满足特定条件时,才会触发断点。
  • F5:启动调试,通常与F9配合使用,用于直接跳转到下一个断点处。
  • F10:逐过程执行,用于处理一个完整的过程,如函数调用或单条语句。
  • F11:逐语句执行,每次执行一条语句,能深入函数内部观察执行逻辑的细节,想探究细节需要使用F11,用F10则跳过。

4、监视和内存观察

在调试过程中,若要观察代码执行时变量的值,可以使用监视和内存窗口来进行观察,但观察的前提条件是已经开始调试。

例1:

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int num = 100;
	char c = 'w';
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		arr[i] = i;
	}
	return 0;
}

4.1、监视

开始调试后,在菜单栏中【调试】->【窗口】->【监视】,打开任意一个监视窗口,输入想要观察的对象即可。

打开监视窗口,如图:

在这里插入图片描述

在监视窗口中观察对象:

在这里插入图片描述

4.2、内存

如果监视窗口看的不够仔细,也可以观察变量在内存中的存储情况,还是在【调试】->【窗口】->【内存】中。

打开内存窗口:

在这里插入图片描述

在内存窗口中观察数据:
在这里插入图片描述

打开内存窗口后,在地址栏输入:arr,就能观察到该地址处的数据。如图:

在这里插入图片描述

除此之外,在调试的窗口中还有:自动窗口、局部变量、反汇编、寄存器等窗口。

5、调试举例

例1:在VS2022、X86、Debug的环境下,编译器不做任何优化的话,下面代码执行的结果是多少?

#include <stdio.h>
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("hello\n");
	}
	return 0;
}
输出:
死循环

调试观察,上面程序的内存布局如图:

在这里插入图片描述

  • 1、栈区内存的使用习惯是从高地址向低地址使用的,所以变量i的地址是较大的。arr数组的地址整体是小于i的地址。但需注意,栈区的具体使用习惯依赖于编译器的实现,如在VS的X64模式或Release版本中,这一习惯可能相反。
  • 2、数组在内存中的存放是:随着下标的增长,地址是由低到高变化的。
  • 3、按照图中的内存布局,随着数组下标的增长,往后越界就有可能覆盖到i,这样就可能造成死循环。

为什么iarr数组之间恰好空出来2个整型的空间呢?

这个是巧合,不同的编译器可能会在中间空出不同大小的空间,因为代码中变量的内存和地址分配是由编译器决定的,所以不同编译器之间存在差异。因此,这个问题与环境相关。

从这个例子中,能体会到调试的重要性,只有调试才能观察到程序内部执行的细节。就像医生通过给病人进行B超、CT等检查来找出病因一样。

例2:在数组传参,调试进入函数,如何在监视窗口观察数组的内容?

#include <stdio.h>
void test1(int d[])
{
    
}
int main()
{
	int data[10] = { 1,3,5,7,9 };
	test1(data);
	return 0;
}

调试如图:

在这里插入图片描述

#include <stdio.h>
void test2(int a[3][5])
{

}
int main()
{
	int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
	test2(arr);
	return 0;
}

调试如图:

在这里插入图片描述

调试过程中,要做到心里有数,也就是程序员自己心里要清晰的知道希望代码怎么执行,然后再去看代码有没有按照我们预定的路线在执行。

调试是需要反复去动手练习的,调试是可以增加程序员对代码的理解和掌控的,掌控了调试的能力,就能看到本质,就像能给程序做B超一样,对程序内部一览无余。

6、编程常见错误归类

6.1、编译型错误

编译型错误一般都是语法错误,这类错误一般看错误信息就能找到一些蛛丝马迹的,双击错误信息也能初步的跳转到代码错误的地方或者附近。编译错误,随着语言的熟练掌握,会越来越少,也容易解决。

在这里插入图片描述

6.2、链接型错误

看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是因为:

  • 标识符名不存在
  • 拼写错误
  • 头文件没包含
  • 引用的库不存在

在这里插入图片描述

6.3、运行时错误

运行时错误,是千变万化的,需要借助调试,逐步定位问题,调试解决的是运行时问题。


完。

  • 31
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值