VS调试技巧

                    Hi~!这里是奋斗的小羊,很荣幸各位能阅读我的文章,诚请评论指点,关注+收藏,欢迎欢迎~~     

                        💥个人主页小羊在奋斗

                        💥所属专栏C语言   

        本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为同样是初学者的学友展示一些我的学习过程及心得。文笔、排版拙劣,望见谅。

                                1、调试

                                2、Debug 和 Release

                                3、VS调试快捷键

                                4、监视和内存观察

                                5、调试举例

                ​​​​​​​        ​​​​​​​        6、编程常见错误

1、调试

        当我们发现写的程序中存在问题(bug)的时候,接下来就要找到问题并修复,这个过程就叫调试(debug)。调试一个程序,首先要承认出现了问题,然后通过手段定位问题的位置,可以是逐过程的调试,也可以屏蔽部分代码等,找到问题所在的位置,然后确定产生问题的原因,再修改代码重新测试。

        调试是一个强大的功能,有时候我们百思不得其解的问题,通过调试就能一目了然,很快的找出问题所在。另外,有些问题就算我们想破脑袋都想不出个所以然来,只能通过调试来知道其内部原因。

2、Debug 和 Release

        

        在VS上,能看到有 Debug 和 Release 两个选项,分别是什么意思呢?

        Debug 称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序;

        Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户更好的使用。Release 版本是不包含调试信息的。

3、VS调试快捷键

        在VS中,有很多的调试快捷键便于程序员调试代码,提高了调试效率。

        3.1环境准备

        首先,我们得设在 Debug 环境下。

        3.2调试快捷键

        F9:创建断点和取消断点;

        断点的作用是:在程序的任意位置设置断点使得程序执行到想要的位置暂停执行,再使用F10和F11快捷键观察代码的执行细节;

        条件断点:断点还可以设置条件,满足这个条件才会出发断点;

        F5:启动调试,经常用来直接跳到下一个断点处,一般是和F9配合使用;

        F10:逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句;

        F11:逐语句,就是每次都只执行一条语句,在函数调用的地方可以进入函数内部;

        CTRL + F5: 开始执行不调试。

4、监视和内存观察

​​​​​​​        我们在调试的过程中,如果想要观察代码执行过程中变量值的变化,有哪些办法呢?这些观察的前提条件是在开始调试后观察。

        4.1监视

        

 

        开始调试后,在菜单栏中【调试】—> 【窗口】—> 【监视】,打开任意一个监视窗口,输入想要观察的对象即可,加上取地址符还可以看到地址。 

        4.2内存

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

         

5、调试举例

        5.1示例一

         在VS2022、x86、Debug 环境下,执行下面代码,结果会是什么呢?

        我们可以看到,数组是越界非法访问的,那么这一小段代码的执行结果是打印10个“加油”呢?还是13个“加油”呢?还是其他不可预测的结果? 

                                                                                                                         

        事实上当我们运行起来后屏幕上会一直打印“加油”,好像跟我们的猜测有很大的差别。那么为什么会发生这样的现象呢?像这种情况我们光靠脑子想是想不出来个所以然的,我们需要借助强大的调试功能。

         

        当我们按F10逐过程一直到 i 的值为10的时候,我们把 arr[10]、arr[11]、arr[12] 添加到监视,再按F10(此时已经是非法访问)的时候我们看到,arr[12] 的值一直跟着 i 的值在变化,这是为什么呢?我们把 i 的地址和 arr[12] 的地址添加到监视看一下:

         i 的地址跟 arr[12] 的地址是一样的,所以说 i 跟 arr[12] 是等价的。当我们继续按F10给 arr[12] 赋0值后,i 的值也跟着变为0。如此循环下去,程序就陷入了死循环。

         在之前的文章中我们已经简单了解过内存中的数据存储(数据和变量),我们这里再补充一下。

        (1)局部变量存储在内存中的栈区,而栈区的使用习惯是从高地址向低地址使用的,所以我们先创建的变量 i 的地址是比较大的,arr 数组的地址整体是小于 i 的地址的;

        (2)一维数组中我们知道,数组在内存中的存储规则是:随着下标的增大地址由低到高变化。再结合下面的图我们就能很好的理解。

         当数组 arr 的下标逐渐增大,地址越来越接近变量 i ,当下标为12的时候,arr[12] 就跟变量 i 的地址重合了,这时 arr[12] 就跟变量 i 是一个值,操作 arr[12] 就是操作变量 i 。

        至于为什么 arr 数组和变量 i 之间恰好是2个 int 型的空间,这纯属是巧合,在不同的编译器中两者之间空出来的大小是不一样的。

        注意:栈区的默认使用习惯是先使用高地址再使用低地址,但是这个具体还要看编译器的实现,比如:在VS上切换x64,这个使用的顺序就是相反的,在 Release 版本中,这个使用顺序也是相反的。

        上面的程序是在x64环境下的,我们可以看到 arr[0] 的地址都比变量 i 的地址大。 

                5.2示例二

        看下面的代码:

//【编写一个函数实现n的k次方,使用递归实现。】

#include <stdio.h>

int fun(int x, int y)
{
	if (1 == x || 0 == y)
	{
		return 1;
	}
	else if (0 == x && 0 != y)
	{
		return 0;
	}
	else
	{
		return x * fun(x, y--);
	}
}

int main()
{
	int n = 0;
	int k = 0;
	scanf("%d %d", &n, &k);
	int m = fun(n, k);
	printf("%d\n", m);
	return 0;
}

        像我一样的新手乍一看可能觉得没什么问题(如果你已经看出了问题所在,还请不要声张),但是运行起来才发现有问题,程序陷入了死循环。我们来调试一下哪里出了问题:

        第一步:F10开始调试;

        第二步:输入我们想要观察的变量;

        第三步:通过不断F10来观察变量值的变化,当变量的值与我们想要的不一样的时候,我们就找到了问题所在;

             假如我们要求2的4次方: 

   

         

        当我们不断F10,执行了 return x * fun(x,y--);这条语句后,突然弹出了栈溢出的提示(栈溢出简单说是因为函数调用层级过深引起的,这里我们不作过多介绍,后面会有相关文章),那么问题应该就在这条语句中。

        第四步:在知道问题所在后,就要分析其原因。

        我们的想法是通过让形参 y 的值自减,来实现 x 自乘的次数。但我们知道,y-- 和 --y 确实能实现自减的效果,但两者是有很大区别的(如果你还不知道,罚你去看这篇文章 —> C语言基础),我们改正过来继续调试。

        可以看到,在我们修改了错误后就实现了 y 不断自减的效果,现在我们写的程序就能执行我们想要的任务了。 

        总结:通过上面两个示例,想告诉大家调试是一个很强大的功能,就算是编程高手也不见得总是一次就能写出正确的代码,在不断地修改中才能解决所有的问题,而调试无疑是最好的助手。作为程序员,不使用调试或不会调试是万万不可的。同时,调试还可以让开发者深入了解程序的运行机制,提高编程技能和经验。

6、编程常见错误

        6.1编译型错误        

        编译型错误一般是语法错误,这类错误一般看错误信息就能找到一些蛛丝马迹,双击错误信息也能初步跳转到代码错误的地方或附近。编译型错误,随着语句的熟练,会越来越少,也容易解决。例如缺少分号、使用未定义的变量等:

         6.2链接型错误

        看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是因为标识符名不存在、拼写错误、头文件没包含、引用的库不存在等。

         6.3运行时错误

        运行时错误,是千变万化的,也是最难解决最令我们头疼的,需要借助调试,逐步定位问题。调试解决的是运行时问题,也就是说调试的前提是你的程序得先能运行起来,没有简单的语法和链接错误,然后才能调试。

                                   点击跳转主页—> 💥个人主页小羊在奋斗

  • 32
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值