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环境 中的结果展示:
#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版本会对代码进行各种优化,因此这种版本也并不适合我们进行调试。
那编译器进行了哪些优化呢?再来看看下面这段代码在两种环境下的结果展示:
#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. 运行时错误:借助调试,逐步定位问题。最难搞。
最后就是如果我们想要在将来写出一份优秀的代码,一定要熟练掌握调试技巧,并且多使用快捷键,提升效率。只有多动手,尝试调试,我们才能有进步!!
相关文章:

Visual Studio的实用调试技巧总结
对于很多学习编程的老铁们来说,是不是也像下面这张图一样写代码呢? 那当我们这样编写代码的时候遇到了问题?大家又是怎么排查问题的呢?是不是也像下面这张图一样,毫无目的的一遍遍尝试呢? 这篇文章我就以 V…...

graphrag学习总结
学习视频:b站链接 项目链接 GraphRAG 的基本概念 Document(文档):系统中的输入文档。这些文档要么代表CSV中的单独行,要么代表单独的txt文件。 TextUnit(文本块):要分析的文本块。…...
专题:贪心算法(已完结)
1.分发饼干 方法一:用最大的胃口 找到最大的饼干(先遍历胃口) class Solution { public:int findContentChildren(vector<int>& g, vector<int>& s) {// 主要思路 用最大的饼干找最大的胃口sort(g.begin(),g.end());so…...

Hadoop的三种运行模式:单机模式、伪分布式模式和完全分布式模式
单机模式 单机模式是Hadoop最简单的运行模式。在单机模式下,所有Hadoop组件都运行在单个机器上,包括HDFS、MapReduce等。由于只有一个节点参与计算,单机模式适用于开发和测试阶段,不适合用于处理大规模数据。在单机模式下…...

JavaScript将array数据下载到Excel中
具体代码如下: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widt…...
【前端】Bootstrap:快速开始
Bootstrap 是一个功能强大且易于使用的前端框架,专门用于创建响应式和移动优先的网页。学习Bootstrap不仅可以帮助你快速构建现代网页,还可以提升你对前端开发流程的理解。本教程将从基础概念开始,逐步引导你掌握Bootstrap,并通过…...

文献阅读(222) VVQ协议死锁
题目:VVQ: Virtualizing Virtual Channel for Cost-Efficient Protocol Deadlock Avoidance时间:2023会议:HPCA研究机构:KAIST request-reply协议死锁如下图所示,每个node收到request之后发送reply,但是想…...

Node.js管理工具NVM
nvm(Node Version Manager)是一个用于管理多个 Node.js 版本的工具。以下是 nvm 的使用方法和一些常见命令: 一、安装 nvm 下载 nvm: 地址:https://github.com/coreybutler/nvm-windows/releases访问 nvm 的 GitHub 仓…...
云原生后端
云原生后端(Cloud-Native Backend)是指在云计算环境中,利用云原生技术(如容器、微服务、服务网格等)构建和部署后端应用程序的一种方法。以下是对云原生后端的详细讲解: 1. 定义 云原生是一种设计和构建应…...

充电宝哪个品牌值得买?2024年五款靠谱充电宝推荐
哪个品牌充电宝值得买?用过这么多款充电宝,个人还是觉得充电快、小巧便携的充电宝使用会更加的方便!在当今快节奏的生活中,手机已成为我们不可或缺的伙伴。然而,随着智能手机功能的日益强大,电池续航问题也…...

YOLOv11对比YOLOV8网络结构变化分析,帮助你真正的理解和学习yolo框架
本文在大佬的文章YOLOv11 | 一文带你深入理解ultralytics最新作品yolov11的创新 | 训练、推理、验证、导出 (附网络结构图)基础上做了一些补充。 一、YOLOv11和YOLOv8对比 二、YOLOv11的网络结构图 下面的图片为YOLOv11的网络结构图。 三、YOLOv11…...
弃用RestTemplate,RestClient真香!
在Spring框架的发展历程中,RestTemplate作为发起HTTP请求的同步API,曾经扮演着举足轻重的角色。然而,随着技术的不断进步和微服务架构的普及,RestTemplate的局限性逐渐显现,尤其是在处理高并发和异步请求时。因此&…...
electron-vite_10electron-updater软件更新
网很多electron-updater更新文章,这里只简单写一下演示代码; 为什么选择 electron-updater插件可以自动更新应用程序,同时支持多个平台;比官方要强; 官方的autoUpdater仅支持macOS 和 Windows 自动更新; 注意是自动,直接更新那种; 脚手架中是…...
React native之全局变量存储AsyncStorage
AsyncStorage是React native中对变量,对象进行全局存储,读取的异步使用对象。以key值进行存储。但是只能存储字符串数据,想存储对象,可把对象JSON进行序列化存储,读取的时候再转成JSON对象。 AsyncStorage.getItem()-…...

获取vue实例
需要注意的是,无论通过哪种方式获取元素,如果元素为 vue 组件,则需要在子组件中使用 defineExpose 进行暴露。 在父组件中,我们静态绑定 childRef: 在子组件中,我们需要通过defineExpose函数,手…...
基于Python实现电影推荐系统
电影推荐系统 标签:Tensorflow、矩阵分解、Surprise、PySpark 1、用Tensorflow实现矩阵分解 1.1、定义one_batch模块 import numpy as np import pandas as pddef read_and_process(filename, sep ::):col_names [user, item, rate, timestamp]df pd.read_cs…...

【linux】进程理解
🔥个人主页:Quitecoder 🔥专栏:linux笔记仓 目录 01.进程的基本概念进程的组成部分进程的特性进程的状态 02.PCBPCB的组成部分task_structtask_struct 的主要组成部分 03.进程属性查看进程 04.通过系统调用创建进程-fork初识工作…...
文件IO练习1
题目一: 1、使用fread和fwrite完成两个文件的拷贝,要求源文件和目标文件由外界输入 实现代码: #define LEN_BUF 256int main(int argc, const char *argv[]) {if(argc ! 3){fprintf(stderr,"程序入参输入有误\n");return -1;}FILE…...
c++ std::future 和 std::promise 的实现工作原理简介
为了便于理解 std::future 和 std::promise 的实现工作原理,我们可以创建一个简化的版本。这包括共享状态、Promise 设置值、Future 获取值的核心机制。我们的示例代码将实现 SimplePromise 和 SimpleFuture 两个类,二者通过一个共享状态实现线程间的通信…...

MATLAB(Octave)混电动力能耗评估
🎯要点 处理电动和混动汽车能耗的后向和前向算法模型(simulink),以及图形函数、后处理函数等实现。构建储能元数据信息:电池标称特性、电池标识符等以及静止、恒定电流和恒定电压等特征阶段。使用电流脉冲或要识别的等效电路模型类型配置阻抗…...

Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...

遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...