VS实用调试技巧

news2024/9/22 13:32:59

一.什么是BUG🐛

Bug一词的原意是虫子,而在电脑系统或程序中隐藏着的一些未被发现的缺陷或问题,人们也叫它"bug"。这是为什么呢?这就要追溯到一个程序员与飞蛾的故事了。

Bug的创始人格蕾丝·赫柏(Grace Murray Hopper),是一位为美国海军工作的电脑专家,也是最早将人类语言融入到电脑程序的人之一。而代表电脑程序出错的“bug” 这名字,正是由赫柏所取的。1947年9月9日,赫柏对Harvard Mark II设置好17000个继电器进行编程后,技术人员正在进行整机运行时,它突然停止了工作。于是他们爬上去找原因,发现这台巨大的计算机内部一组继电器的触点之间有一只飞蛾,这显然是由于飞蛾受光和热的吸引,飞到了触点上,然后被高电压击死。所以在报告中,赫柏用胶条贴上飞蛾,并把“bug”来表示“一个在电脑程序里的错误”,“Bug”这个说法一直沿用到今天。

格蕾丝·赫柏的报告

二.调试及其重要性📝

2.1 什么是调试

所有发生的事情都一定有迹可循,如果问心无愧,就不需要掩盖也就没有迹象了,如果问心有愧,就必然需要掩盖,那就一定会有迹象,迹象越多就越容易顺藤而上,这就是 推理的途径。顺着这条途径顺流而下就是犯罪,逆流而上,就是真相。

而我们程序员就好比一个侦探,一个用来寻找bug,修改bug的侦探。人们将这个过程叫做"Debug"(中文称作"调试"),意即"捉虫子"或"杀虫子"。每一次调试都是尝试破案的过程。

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

2.2 调试的基本步骤

  • 发现程序错误的存在

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

  • 确定错误产生的原因

  • 提出纠正错误的解决办法

  • 对程序错误予以改正,重新测试

本文将详细介绍在windows系统的 VS环境下的调试过程。

2.3 Debug与Release的介绍

在VS中,我们会发现我们的程序可以在两个环境下运行,这两个环境就是Debug版本和Release版本,它们二者有何区别呢?

VS中的Debug与Release

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

我们可以分别在两种环境下编译代码生成对应的可执行程序如下:

显然,Release版本的可执行文件比Debug版本的小很多,说明编译器对其作了优化。

因此,我们日常所说的调试是在Debug版本的环境下进行的,这是因为Release版本无法进行调试,其调试信息被编译器优化了。而对于测试人员来说,由于要站在用户的角度上来测试程序是否能正常使用,因此测试是在Release版本的环境下进行的


那么,Release版本下编译器可能会做什么优化呢?请看如下代码:

#include <stdio.h>

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

我们很容易就发现对数组进行了越界访问,当程序运行起来时应该会崩溃。但是在Debug环境下我们发现程序并没有崩溃而是陷入了死循环:

我们运行调试代码转到反汇编如下:

我们知道,数据在栈上的开辟是从高地址向低地址处开辟的,因此在Debug环境下变量i的地址比数组arr的地址高。而在数组内部数据的存储是从低地址向高地址的,因此首元素地址arr是在数组所在空间的低位,如下:

我们很惊讶的发现(arr+12)就是变量i的地址。当循环过程中i等于12时,此时将arr[i]改为0就等价于将i的值修改为0,然后i++后i等于1小于12,继续进行循环,依次反复形成了死循环。如下:

整个过程简化图如下:


而在Release版本的环境下程序并不会出现死循环的问题:

我们可以打印出此时变量i和数组在栈上的地址如下:

我们发现此时变量i被编译器优化到低地址处,arr[12]存储的值就不是i了,便不会出现死循环的情况。

综上,以上代码在Release版本下,编译器使 变量在内存中开辟的顺序发生了变化,影响到了程序执行的结果,这便是优化带来的好处。

三.windows环境下调试介绍✨

首先第一步,我们需要将环境切换为Debug版本,才能进行调试

3.1 常用快捷键介绍

以下是在调试过程中最为常用的几个快捷键:

快捷键

功能

Ctrl+F5

开始执行而不进行调试。用于想让程序直接运行起来而不调试时。

F9

作用:创建断点和取消断点

断点:可以使程序在任何你想停下的地方停止执行,继而一步步执行下去。我们可以在任何地方设置断点,一个程序也可以有多个断点

F10

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

F11

逐语句,每次只执行一条语句,我们可以通过这个快捷键使我们的执行逻辑进入函数内部(这是F10所不具备的,F11是我们在调试过程中最常用的)

F5

启动调试到下一断点处,需要配合F9进行使用,如果程序没有断点则无异于Ctrl+F5

快捷键用法

我们还可以对某一断点设置停止条件,方法是 右键断点->单击条件->输入断点条件->关闭
例如:我们需要让程序停止在第4次循环处,我们可以输入i==4
此时单击F5运行到断点,我们查看自动窗口发现程序在停止时i的值为4

3.2 在调试过程中查看程序当前信息

开启调试后,我们可以在VS上方的调试->窗口看到许多用来查看数据信息的窗口:

3.3.1 监视窗口

通过监视窗口我们可以查看我们想要查看的 变量甚至是表达式的值在程序运行过程中的变化,十分灵活,这是我们调试中用得最多的窗口之一。如下:

3.3.2 自动窗口

打开自动窗口后,编译器会将一些可能需要观察的变量显示在窗口中,较为方便。其缺陷是可能无法显示我们真正需要观察的变量,并且 无法灵活显示表达式等的值。如下:

3.3.3 监视窗口

打开局部变量变量窗口,会将程序中的所有 局部变量全部显示出来。如下:

3.3.4 调用堆栈窗口

通过调用堆栈,可以清晰的反应 函数的调用关系以及当前调用所处的位置。如下:
通过调用堆栈窗口,我们可以发现,show函数栈帧在main函数栈帧之上,即 show函数是由main函数调用的。并且可以看出此时show函数运行到第20行。

3.3.5 内存窗口

通过内存窗口我们可以看到内存中的信息,可以 观察变量在内存中的存储情况。如下:

3.3.6 反汇编

我们可以查看当前程序转化后的汇编代码,进而从更底层的角度观察程序的执行过程。如下:

3.3.7 寄存器

通过寄存器窗口,我们可以观察在当前环境下CPU内的寄存器的使用信息,如ebp栈底寄存器、esp栈顶寄存器等等。

3.3 调试实例

我们上面通过调试分析了数组越界陷入死循环的问题。下面,我们再通过一道实例来掌握调试的技巧:

实现代码:求 1!+2!+3! ...+ n! ;不考虑溢出。我们可能会写出以下代码:
#include<stdio.h>
int main()
{
    int i = 0;
    int sum = 0;//保存最终结果

    int n = 0;
    int ret = 1;//保存n的阶乘

    scanf("%d", &n);
    for(i=1; i<=n; i++)
    {
        int j = 0;
        for(j=1; j<=i; j++)
        {
            ret *= j;
        }
        sum += ret;
    }
    printf("%d\n", sum);
    return 0;
}
当你输入3时,理论上应该输出9,但实际上程序却输出了15:
是什么问题导致出错了呢?这就需要我们动手进行调试。在调试之前我们可以先 预测问题的所在,比如 算阶乘时出错求和时出错等等。做到心里有数,而不是盲目的进行调试。

我们可以在ret*=j处设置一个断点,打开监视窗口监视ret和sum观察每个数阶乘的值和累加后的值,如下:

我们单击F10逐过程执行,当外层循环i的值为3时,即计算3的阶乘时,我们发现ret的初始值并非为1,而是2的阶乘。此时再计算3的阶乘,就是2*1*2*3=12 != 6,结果出错:

最后sum+ret的值就为15,与我们的输出相符:

可见,每次在求阶乘时,我们都应该把ret重置为1,正确的代码如下:

int main()
{
    int i = 0;
    int sum = 0;//保存最终结果

    int n = 0;
    int ret = 1;//保存n的阶乘

    scanf("%d", &n);
    for (i = 1; i <= n; i++)
    {
        int j = 0;
        ret = 1; //将ret置1
        for (j = 1; j <= i; j++)
        {
            ret *= j;
        }
        sum += ret;
    }
    printf("%d\n", sum);
    return 0;
}

3.4 实践出真知

  • 任何事情都不可能一蹴而就,一定要多加练习,才能熟练掌握调试技巧。

  • 一个初学者可能80%的时间在写代码,20%的时间在进行调试;而一个程序员,可能80%的时间在进行调试,剩余20%的时间才是在写代码。

  • 随之学习的深入,后续可能会出现很多更加复杂的调试场景,如多线程等。只有打好基础,在未来才能融会贯通,利于不败之地。

  • 多多使用快捷键可以很大程度上提高效率。

四.如何写出便于调试的优秀代码👑

4.1 什么是优秀的代码

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

4.2 常用coding(编码)技巧

1.多使用assert断言,可以告知你出错的位置
2.尽量使用const,避免意外修改
3.养成良好的编码风格
4.添加必要的注释
5.避免编码的陷阱

4.3 实例

试模拟实现一个strcpy函数,尽量用到上述的编码技巧。如下:
char* strcpy(char* dst, const char* src) //const修饰防止对源字符串进行修改
{
    assert(dst && src); //避免传入空指针
    char cur = dst; //保存起始位置
    //将src的字符一个个拷贝到det中,包括'\0'
    while ((*dst++ = *src++)!='\0')
    {
        ;
    }
    return cur; //返回目标字符串,以便链式访问
}

五.编程中常见的错误👀

5.1 编译型错误

编译期间出现的错误。直接看错误提示信息(双击鼠标),解决问题。或者凭借经验就可以搞定。相对来说简单。

5.2 链接型错误

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

5.3 运行时出错

运行期间出现的错误。借助 调试,逐步定位问题。最难搞的一种错误

不要害怕遇到错误,每出现一次的错误就是一次突破自我的机会。
学会积累排错经验,勇于尝试。不经风雨 🌂 ,怎能见彩虹 🌈

以上,就是本期的全部内容啦🌸

制作不易,能否点个赞再走呢🙏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/351691.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

sql复习(视图、Top-N分析、其他数据库对象)

一、视图view 1.视图定义 视图是一种虚表。 视图建立在已有表的基础上, 视图赖以建立的这些表称为基表。 向视图提供数据内容的语句为 SELECT 语句, 可以将视图理解为存储起来的 SELECT 语句。 视图向用户提供基表数据的另一种表现形式。 2.使用视图的好处 控制数据访问 简…

【离线数仓-1-数仓前期建设准备工作】

离线数仓-1-数仓前期建设准备工作离线数仓-1-数仓前期建设准备工作1.数仓概念1. 数据仓库概念2. 数据分类3.数据仓库总视图4.数据仓库项目需求分析5.数仓搭建技术基础分析1.项目技术如何选型&#xff1a;2.框架版本如何选型&#xff1b;3.服务器使用物理机还是云主机&#xff1…

C++:模板初阶(泛型编程、函数模板、类模板)

文章目录1 泛型编程2 函数模板2.1 函数模板概念2.2 函数模板格式2.3 函数模板的原理2.4 函数模板的实例化2.5 模板参数的匹配原则3 类模板3.1 类模板的定义格式3.2 类模板的实例化1 泛型编程 所谓泛型&#xff0c;也就是通用型的意思。 在以往编写代码时&#xff0c;我们常常…

Greenplum的两阶段提交

注&#xff1a;本文章引自终于把分布式事务讲明白了&#xff01; 在前面的文章中&#xff0c;我们了解了单机库中的事务一致性实现以及分布式事务中的两阶段提交协议。大多数分布式系统都是采用了两阶段提交塄来保证事务的原子性&#xff0c;Greenplum也是采用了两阶段提交&am…

ChatGPT直播课听不够?微软ATP教你系统化入门人工智能

爆火的ChatGPT直播课听不过瘾&#xff1f;系统化AI知识在哪学&#xff1f;国际TOP科技公司AI算法工程师团队来为你支招&#xff01;▍微软AI算法专家助力系统化学习人工智能「微软ATP」Microsoft AI Talent Program是微软&#xff08;亚洲&#xff09;互联网工程院下属的提供专…

【测试】自动化测试02

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录前言 回顾 预告一、常见的元素操作1. 输入文本sendKeys()2. 点击click3. 提交submit&#xff08;通过回车键提交&#xff09;4. 清除clear5. 获取文本getText()6. 获取属性对应的值getAttribute()7. 查看title和ur…

(二十四)ATP应用测试平台——springboot集成fastdfs上传与下载功能

前言 本节内容我们主要介绍一下如何在springboot项目中集成fastdfs组件&#xff0c;实现文件的上传与下载。关于fastdfs服务中间键的安装过程&#xff0c;本节内容不做介绍。fastdfs是一个轻量级的分布式文件系统&#xff0c;也是我们文件存储中常常使用的组件之一&#xff0c…

酷开系统壁纸模式,将氛围感死死拿捏!

古希腊哲学家柏拉图曾经说过&#xff1a;“美感是起于视觉、听觉产生的快感&#xff0c;以人的感官所能达到的范围为极限。”而电视则恰恰就是视觉听觉的完美融合体&#xff0c;当一台开启的电视可以给我们带来视听享受的时候&#xff0c;一台待机状态下的电视又如何取悦于我们…

Kotlin新手教程三(类与继承)

一、类 1.类的声明 与Java中类似&#xff0c;Kotlin中的类声明使用关键字class&#xff1a; class Person {var height: Int185 //规范写法&#xff1a;冒号后需要紧跟一个空格 }当你定义的类没有类体时&#xff0c;可以省略花括号&#xff1a; class Person2.构造函数…

多云和混合云场景下的 API 管理:挑战与选择

作者张超&#xff0c;API7 Cloud 产品负责人&#xff0c;Apache APISIX PMC 成员。 原文链接 一、多云和混合云 如今微服务已经成为最流行的一种软件架构&#xff0c;人们通过自己对业务的理解&#xff0c;和科学方法&#xff08;比如领域驱动设计的理论&#xff09;的加持将…

算法刷题打卡第93天: 最大的以 1 为边界的正方形

最大的以 1 为边界的正方形 难度&#xff1a;中等 给你一个由若干 0 和 1 组成的二维网格 grid&#xff0c;请你找出边界全部由 1 组成的最大 正方形 子网格&#xff0c;并返回该子网格中的元素数量。如果不存在&#xff0c;则返回 0。 示例 1&#xff1a; 输入&#xff1a…

c++11 标准模板(STL)(std::multimap)(九)

定义于头文件 <map> template< class Key, class T, class Compare std::less<Key>, class Allocator std::allocator<std::pair<const Key, T> > > class multimap;(1)namespace pmr { template <class Key, class T…

【论文速递】ACL 2021-CasEE: 一种用于重叠事件抽取的级联解码联合学习框架

【论文速递】ACL 2021-CasEE: 一种用于重叠事件抽取的级联解码联合学习框架 【论文原文】&#xff1a;A Joint Learning Framework with Cascade Decoding for Overlapping Event Extraction 【作者信息】&#xff1a;Sheng, Jiawei and Guo, Shu and Yu, Bowen and Li, Qian…

Python登陆系统

前言 #源码见文末公众号哈# 登录系统 一个简单的登录系统包含了登录账户、注册账户、修改密码以及注销账户的操作。 1. 登录账户 登录系统主要需要判断账户是否存在&#xff0c;不存在就注册一个账户&#xff0c;如果第一次登录系统&#xff0c;我们需要先新建一个文件&…

解密Teradata与中国市场“分手”背后的原因!国产数据库能填补空白吗?

2月15日&#xff0c;西方的情人节刚刚过去一天&#xff0c;国内IT行业就爆出一个大瓜。 继Adobe、甲骨文、Tableau、Salesforce之后&#xff0c;又一个IT巨头要撤离中国市场。 Teradata天睿公司官宣与中国市场“分手”&#xff0c;结束在中国的直接运营。目前&#xff0c;多家…

MySQL Study Notes Design in 2023

文章目录1 概述1.1 MySQL相关概述1.2 数据模型1.3 SQL分类2 数据库设计-DDL2.1 约束2.2 字段3 数据库操作-DML3.1 增加(insert)1 概述 1.1 MySQL相关概述 数据库&#xff1a;英文为 DataBase&#xff0c;简称DB&#xff0c;它是存储和管理数据的仓库。 数据库管理系统&#xf…

为什么重写equals必须重写hashCode

关于这个问题&#xff0c;看了网上很多答案&#xff0c;感觉都参差不齐&#xff0c;没有答到要点&#xff0c;这次就记录一下&#xff01; 首先我们为什么要重写equals&#xff1f;这个方法是用来干嘛的&#xff1f; public boolean equals &#xff08;Object object&#x…

Wallys|5 GHz 802.11ax|Does wallys QCN9074-5G module support W56?

Wallys|5 GHz 802.11ax|Does wallys QCN9074-5G module support W56?-Does wallys QCN9074-5G module support W56?-The answer is---sure!In this article,we’ll introduce about the W56 at 11ax 5GHz.If you realized this article is helpful, kindly help re-post / co…

《C陷阱与缺陷》----词法“陷阱”

导言&#xff1a; 由于一个程序错误可以从不同层面采用不同方式进行考察&#xff0c;而根据程序错误与考察程序的方式之间的相关性&#xff0c;可以将程序错误进行划分为各种陷阱与缺陷&#xff1a; ①.词法“陷阱” ②.语法“陷阱” ③.语义“陷阱” ④.连接问题 ⑤.库函数问…

Microsoft Office 2021 / 2019 Direct Download Links

前言 微软Office在很长一段时间内都是最常用和最受欢迎的软件。从小型创业公司到大公司,它的使用比例相当。它可以很容易地从微软的官方网站下载。但是,微软只提供安装程序,而不提供完整的软件供下载。这些安装文件通常比较小。下载并运行后,安装的文件将从后端服务器安装M…