Visual Studio的实用调试技巧总结

对于很多学习编程的老铁们来说,是不是也像下面这张图一样写代码呢?

那当我们这样编写代码的时候遇到了问题?大家又是怎么排查问题的呢?是不是也像下面这张图一样,毫无目的的一遍遍尝试呢?

这篇文章我就以 Visual Studio 2022编译器为例,带大家了解在Visual Studio2022编译器上是 如何进行调试C语言代码的。

1. 调试是什么?有多重要

1.1 调试的概念

**调试是什么:**调试(英文:Debugging / Debug),又称除错,是发现和减少计算机程序或电子仪器设备中程序错误的一个过程。

1.2 调试的基本步骤

1.发现程序错误的存在。

2.以隔离、消除等方式对错误进行定位。

3.确定错误产生的原因。

4.提出纠正错误的解决办法。

5.对程序错误予以改正,重新测试。

1.3 Debug和Release的介绍

Debug:通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。
Release: 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。(该版本不方便程序员调试)。

1.4 Debug和Release版本的对比

因此,我们平常在编译器中写代码时,都是用的Debug版本。并且我们说调试就是在Debug版本的环境中,找代码中潜伏的问题的一个过程。

对于如下同一份代码,我们来看看在 Debug环境 和 Release环境 中的结果展示:

cpp 复制代码
#include<stdio.h>

int main()
{
	int arr[] = { 1,2,3,4,5,6 };
	int* p = arr;

	printf("%d\n", *(p + 2));

	return 0;
}

Debug环境中:

Release环境中:

由结果中可以看出,Release环境下的执行文件(即 .exe文件)会比 Debug环境下的执行文件小好几倍,这是因为Release版本会对代码进行各种优化,因此这种版本也并不适合我们进行调试。

那**编译器进行了哪些优化呢?**再来看看下面这段代码在两种环境下的结果展示:

cpp 复制代码
#include<stdio.h>

int main()
{
	int i = 0;
	int arr[10] = { 0 };

	for (i = 0; i <= 12; i++)
	{
		arr[i] = 0;
		printf("hahaha\n");
	}
	return 0;
}

同样的代码,在Debug环境中死循环,在Release环境下却能正常运行。这时我们通过调试看看这段代码在两种环境下的反汇编展示对比:

观察两种环境下的汇编指令可以看出,Release版本会删除一些不必要的指令,从而使得编译器在运行代码时速度上是最优的。

这时候也许有老铁会疑惑为什么这段代码在Debug环境下死循环报错**(依赖于编译器的环境,在x86环境下会死循环,x64环境下会报错)呢?**下面我就给大家仔细讲讲。

首先要知道的是,局部变量在内存中的栈区上开辟空间的,而栈区内存空间的使用习惯:先使用高地址处的空间,再使用低地址的空间 (在不同的编译器上有所区别), 如下图所示

数组,随着下标的增长,地址是由低到高变化的,且数组内存是连续存储的。 因此上述代码中通过for循环对数组进行越界访问,并对数组元素重新赋值为0,这样就可能会导致arr[10] ------ arr[12]中所开辟的内存空间 与 局部变量i 所开辟的内存空间重叠,从而导致 变量i 的值被修改为0,进而陷入死循环。

这也提醒我们在对数组进行访问时千万不能越界访问呀!!

2. Windows环境调试介绍

在对代码进行调试前,一定要将编译器的环境设置为 Debug版本,这样才能正常调试!

2.1 Visual Studio中常用的一些快捷键

F5 ------ 启动调试,经常用来直接跳到下一个断点处**(切换断点)**。

F9 ------ 创建断点和取消断点 。断点的重要作用,可以在程序的任意位置设置断点。这样就可以使得程序在想要的位置随意停止执行,继而一步步执行下去。

F10 ------ 逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句。(逐过程简单的说,就是当遇到函数调用时,直接就执行完成了,而不会进入到函数内部。)

F11 ------ 逐语句,就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部(这是最常用的)。(逐语句简单点说,就是当遇到调用函数时,会进入函数内部逐一执行每条语句。)

CTRL + F5 ------ 开始执行不调试,如果你想让程序直接运行起来而不调试就可以直接使用。

如果直接**按 F5 没用,就试试 Fn+F5,**其他快捷键也是一样的。如果忘记这些快捷键了,Visual Studio上也有提示的。

当进行调试后,就能看到以下窗口,记住一定要调试启动后才能看到

3. 如何写出好(易于调试)的代码

对于一份优秀的代码,通常会有以下几个特性:

1. 代码运行正常。
2. bug很少。
3. 效率高。
4. 可读性高。
5. 可维护性高。
6. 注释清晰。
7. 文档齐全。

4. 常见的coding技巧

  1. 使用assert 。

  2. 尽量使用const。

  3. 养成良好的编码风格。

  4. 添加必要的注释。

  5. 避免编码的陷阱。

这里给大家讲解一下第一点assert。

assert是一个库函数(调用是需要包含头文件:#include<assert.h>),又为 断言 ------ 只要不符合assert内的判断则会报错,反之则代码正常运行。

举例如下:

当我们用 assert 来判断所传的地址是否为空地址时,如果不为空,则代码正常运行,反之则报错(Assertion failed: p != NULL),这样通过assert函数就能很好的指出代码的问题。

5. 常见的编译错误

  1. 编译型错误:直接看错误提示信息(双击),解决问题。或者凭借经验就可以搞定。相对来说简单。(一般找到的都是语法问题)

  2. 链接型错误:看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是标识符名不存在或者拼写错误。

  3. 运行时错误:借助调试,逐步定位问题。最难搞。

最后就是如果我们想要在将来写出一份优秀的代码,一定要熟练掌握调试技巧,并且多使用快捷键,提升效率。只有多动手,尝试调试,我们才能有进步!!

相关推荐
为什么每天的风都这么大20 分钟前
Vscode/Code-server无网环境安装通义灵码
ide·vscode·阿里云·编辑器·ai编程·code-server
yqcoder23 分钟前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
baivfhpwxf202333 分钟前
C# 5000 转16进制 字节(激光器串口通讯生成指定格式命令)
开发语言·c#
许嵩6636 分钟前
IC脚本之perl
开发语言·perl
长亭外的少年1 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
直裾1 小时前
Scala全文单词统计
开发语言·c#·scala
心仪悦悦1 小时前
Scala中的集合复习(1)
开发语言·后端·scala
JIAY_WX1 小时前
kotlin
开发语言·kotlin
代码小鑫1 小时前
A043-基于Spring Boot的秒杀系统设计与实现
java·开发语言·数据库·spring boot·后端·spring·毕业设计