1-05使用VS调式程序

一、概述

写代码总不是一帆风顺的,总会碰到一些问题,检查问题,排除错误(也就是Debug程序),对于任何程序员而言都是基本功。

我们前面讲过,C语言是一门很容易出错,且自身不提供异常处理机制的语言,这导致程序员难以发现解决问题。

但好在利用集成开发环境Visual Studio写C语言代码时,VS给我们提供了一系列的手段来排查解决问题,这就是VS的Debug模式。本节课的内容很重要,VS的Debug模式不仅是重要的排错手段,也是学习C语言的利器,大家要认真学习。

二、编译和链接的错误

要想使用VS的Debug模式来调试程序,那么首要的前提是程序能够正常启动,也就是程序的问题不能出现在"C源文件 --> C可执行程序"的过程中。

我们都知道,在这个过程中,程序会经历编译(广义)和链接两大过程,所以在讲Debug模式之前,我们先给大家讲一下C程序启动时的编译和链接错误。

注:

VS的Debug模式用于调试运行时的错误,而不是编译或链接错误。编译和链接错误必须在程序运行之前解决。

1. 编译错误

编译错误(Compile-time Errors)出现在代码的编译阶段,表示编译失败。大多数的编译错误,实际上就是疏忽导致的语法错误,比如:

  1. 忘记在语句结尾加上分号。
  2. 小括号、中括号或大括号不匹配。
  3. 类型不匹配,尝试将一个字符串赋值给一个整数类型的变量等。
  4. 使用未声明的变量。

在VS当中运行下列代码:

代码块 1. 编译错误-演示代码

int main(void) {
  a = 100;
  
  return 0;
}

这就是一个典型的编译报错。可以通过查看VS的错误列表窗口来检查这个错误:

图 1. VS-编译错误列表VS-编译错误列表

当然,这个过程需要明确的是:

VS中的C语言代码编译是由内嵌的编译器MSVC完成的,编译错误的报错信息也是这个编译器给出的,然后通过VS的图形界面显示。

2. 链接错误

链接错误(Linking Errors)出现在代码的链接阶段,表示链接失败。链接错误大多和函数调用有关,比如:

  1. 调用函数时,把函数的名字写错了。比如想调用printf函数,但是写成了print。
  2. 忘记包含头文件。比如使用printf函数但忘记写#include语句。
  3. 没有定义一个函数却使用它。

在VS当中启动下列代码:

代码块 2. 链接错误-演示代码

// 没有#include <stdio.h>语句
int main(void){
  printf("hello world!\n");
  
  return 0;
}

这就是一个典型的链接报错。可以通过查看VS的错误列表窗口来检查这个错误:

图 2. VS-链接错误列表VS-链接错误列表

和编译报错信息是编译器给出的一样,链接错误的信息也是链接器给出的。

无论是编译还是链接错误,大多都是简单的语法问题、或主观疏忽导致的。熟练的程序员,程序中即便出现这样的问题,也往往能很快速的解决。

思考:为什么这些与函数调用相关的错误是链接器发出的链接错误呢?为什么不是编译错误呢?

这实际上还是由C程序编译和链接,生成可执行文件的过程决定的。在上一节,我们已经讲解了下图:

图 3. 编译和链接过程图编译和链接过程图

链接过程会将一切需要的外部代码合并到目标文件.o中,其中就包含在源代码中调用的外部函数(比如库函数)。

所以在C程序的编译过程中,并不会检查调用的某个函数是否真正定义,最多只会检查它是否存在声明,然后最多在找不到函数声明时给出一个警告信息(实际上有些编译器连警告都不会给出),编译过程对函数调用不会给出任何报错信息。

而到了链接阶段,链接器会将源代码中所有可能存在的外部引用进行解析,此时如果:

  1. 调用了一个完全不存在的函数或者写错了函数名
  2. 忘记包含头文件

链接器就无法真正将函数的定义合并到目标文件中,此时链接器就会报错。(不报错不行了,再不报错程序就执行了,但此时代码显然无法执行)

用通俗的话来说,C语言为了能够让程序员调用外部的函数(比如库函数),所以在编译阶段不检查函数的定义,最多检查声明。检查函数是否有定义这个事情,是链接器做的。

三、用Debug模式调用时运行时问题(Bug)

如果一个程序没有编译和链接上的问题,那么它最终就能够生成可执行文件。当操作系统加载这个可执行文件到内存并运行时,程序开始执行。

但程序的执行经常会超出或不符合程序员的预期,这就是程序的运行时问题,也就是程序员常说的Bug。

为了解决这些Bug,程序员需要进行程序调试。Debug程序的过程,大体有以下几个步骤:

  1. 大体判断问题的来源、从而模糊的定位问题
  2. 定位具体的错误位置
  3. 修改代码
  4. 验证问题是否得到解决。

这些过程若全靠程序员凭借肉眼、经验去完成,很多时候是比较困难的。但好在现代集成开发环境(IDE)提供了强大的调试工具,如断点、变量观察、单步执行等,帮助程序员更有效地进行Debug。

VS同样也具有这样强大的功能,下面我们一起来学习一下VS的Debug模式。

首先,给出一段用于Debug演示的代码:

代码块 3. Debug模式-演示代码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

void print_rectangle(int line, int column) {
    for (int i = 0; i < line; i++) {
        for (int j = 0; j < column; j++) {
            printf("#");
        }
    }
}

int main(void) {
    printf("hello world!\n");

    int a = 10;
    int b = 20;
    int sum = a + b;

    printf("请键盘输入一个int整数: ");
    int c;
    scanf("%d", &c);
    if (c > 0){
        printf("您输入的数大于0!\n");
    }else if (c < 0) {
        printf("您输入的数小于0!\n");
    }else{
        printf("您输入的数就是0!\n");
    }
    printf("接下来开始打印一个矩形,请输入要打印矩形的行数和列数: \n");
    int line;
    int column;
    scanf("%d", &line);
    scanf("%d", &column);
    print_rectangle(line,column);

  return 0;
}

当然,这一段代码并非完全没有问题,实际上存在一个"bug",我们可以通过Debug模式轻松找到这个问题并修正。

1. 打断点

若想启动Debug模式,首先要做的就是打断点。 如何打断点呢?参考下图:

图 4. Debug模式-打断点Debug模式-打断点

注意:

  1. 如果没有打断点,Debug模式和普通模式启动程序是没有差别的。
  2. 断点意味着代码以Debug模式启动时,程序会在断点处暂停,并即将执行该行语句。比如上图中一个位于main函数,第13行的断点,就表示程序启动后将于13行暂停,并即将执行第13行的代码。

2. Debug模式启动程序

以Debug模式启动程序,在以前章节已经讲过了,也就是下图:

图 5. Debug模式-启动程序Debug模式-启动程序

点击下图中的按钮1,即表示使用Debug模式启动程序。而后面的按钮2,则是正常模式启动程序。

3. 界面窗口布局

为了更好的使用Debug模式,推荐大家按照以下窗口布局:

图 6. Debug模式-窗口布局Debug模式-窗口布局

所有需要的窗口,都可以在**“主界面调试” --> “窗口”**下面找到。

如果搞乱了布局,都可以点击**“主界面窗口” --> “重置窗口布局”**,重置布局。

若希望打开"解决方案资源管理器",可以使用快捷键"Ctrl + Alt + L"。

具体应该如何调整窗口布局?

参考下图:

图 7. Debug模式-调整布局的方式Debug模式-调整布局的方式

自己尝试一下即可,不要害怕弄乱布局,弄乱了重置布局即可。

4. 功能按钮介绍

上面的都是前期准备,Debug模式调试程序的核心是围绕着一系列功能按钮展开的,如下图所示:

图 8. Debug模式-功能按钮介绍Debug模式-功能按钮介绍

逐一介绍:

  1. 按钮1,"继续"按钮。表示立刻跳过当前的断点,转而在下一个断点暂停。若下面没有断点,则执行完毕整个程序,不再暂停。
  2. 按钮2,"显示下一条语句"按钮。顾名思义,显示下一条即将执行的语句。(注意是即将执行,还没执行)
  3. 按钮3,"逐语句(快捷键为F11)"按钮。执行下一条语句,如果碰到函数调用,则进入函数当中。
  4. 按钮4,"逐过程(快捷键为F10)"按钮。执行下一条语句,但会将函数调用作为一条普通语句,直接执行完毕。
  5. 按钮5,"跳出(快捷键为shift + F11)"按钮。直接执行完毕当前函数,回到该函数的调用位置。如果在main函数当中点击此按钮,则会直接将程序整个执行完毕。
  6. 按钮6,"停止调试"按钮。
  7. 按钮7,"重新开始"按钮。

以上,请大家自行尝试使用,熟悉每一个按钮的功能。你找到演示程序中的"Bug"了吗?

注:Debug过程,不仅要查看VS当中的窗口,还需要要配合查看控制台输出以确定程序的运行状态。

四、监视窗口

下面开始介绍上面提到的几个Debug窗口,首先介绍监视窗口。监视窗口是我们日常Debug过程中,最常用的一个窗口。

监视窗口的作用很简单,它允许你在调试过程中实时查看程序中变量的取值,还可以对它们做各种运算操作。这样,程序员就可以观察程序的运行状态,查找错误,修改bug等。

具体而言,在监视窗口中主要可以做到以下几个功能:

  1. **查看变量的值:**可以查看和监控程序中所有变量或者表达式的当前值。
  2. **修改变量的值:**你可以直接在监视窗口中修改变量的值,来观察程序的行为。
  3. **计算表达式:**可以输入和计算表达式,观察表达式的值如何随着程序的执行而改变。
  4. **查看变量的内存地址:**输入"&变量名",即可查看变量的内存地址(值)。

这些操作大家可以自行探索,可以参考下图:

图 9. 监视窗口-使用示例监视窗口-使用示例

五、内存窗口

使用监视窗口得到变量的地址值后,可以将该地址值拖进到内存窗口的地址框,从而查看该变量在虚拟内存空间的内存布局。

如下图所示:

图 10. 内存窗口-使用示例内存窗口-使用示例

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

excel统计分析——LSD多重比较

参考资料&#xff1a;生物统计学 一篇教你搞定显著性差异分析abcd字母标记法 LSD&#xff08;least significant difference&#xff0c;最小显著差数法&#xff09;是R. A. Fisher提出的&#xff0c;又称为Fisher LSD检验法&#xff0c;是最早用于检验各组均数间两两差异的方…

软件工程概论---内聚性和耦合性

目录 一.耦合性 1.内容耦合 2.公共耦合 4.控制耦合 5.标记耦合&#xff08;特征耦合&#xff09; 6.数据耦合 7.非直接耦合 二.内聚性 1.偶然内聚 2.逻辑内聚 3.时间内聚 4.过程内聚 5.通信内聚 6.顺序内聚 7.功能内聚 一.耦合性 耦合性是指软件结构中模块相互…

java常用应用程序编程接口(API)——String概述及使用案例

前言&#xff1a; 开始学到api的String&#xff0c;整理下心得。打好基础&#xff0c;daydayup! API&#xff1a; API是什么&#xff1f; API&#xff08;Application Programming Interface&#xff09;又名应用程序编程接口。是别人编好的程序的合集。 为什么要使用API&…

YOLOv5改进 | 2023Neck篇 | 利用Gold-YOLO针对小目标进行检测(附完整修改教程 + 代码)

一、本文介绍 本文给大家带来的改进机制是Gold-YOLO利用其Neck改进v8的Neck,GoLd-YOLO引入了一种新的机制——信息聚集-分发(Gather-and-Distribute, GD)。这个机制通过全局融合不同层次的特征并将融合后的全局信息注入到各个层级中,从而实现更高效的信息交互和融合。这种…

关于订单超时后用户却已支付的解决方案

场景: 一般我们下的一些订单&#xff0c;比如电商平台、外卖平台的都是有超时时间的&#xff0c;默认一般15或者30分钟&#xff0c;那么超时未支付订单会被自动取消&#xff0c;那么如果刚好有人在关闭订单的时候去支付成功了&#xff0c;或者说支付平台延迟了一下&#xff0c;…

开启Android学习之旅-5-Activity全屏

Android 两种方式设置全屏&#xff1a; 1. 第一行代码中的方法 通过 getWindow().getDecorView()方法拿到当前Activity的DecorView,再调用 setSystemUiVisibility() 方法来改变系统UI的显示&#xff0c;这里传入了 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 和 View.SYSTEM_UI_…

【EAI 005】EmbodiedGPT:通过具身思维链进行视觉语言预训练的具身智能大模型

论文描述&#xff1a;EmbodiedGPT: Vision-Language Pre-Training via Embodied Chain of Thought 论文作者&#xff1a;Yao Mu, Qinglong Zhang, Mengkang Hu, Wenhai Wang, Mingyu Ding, Jun Jin, Bin Wang, Jifeng Dai, Yu Qiao, Ping Luo 作者单位&#xff1a;The Universi…

vue3 封裝一个常用固定按钮组件(添加、上传、下载、删除)

效果图 这个组件只有四个按钮&#xff0c;添加&#xff0c;上传、下载、删除&#xff0c;其中删除按钮的颜色默认是灰色&#xff0c;当表格有数据选中时再变成红色 实现 组件代码 <script lang"ts" setup> import { Icon } from /components/Icon/index im…

PCL 格网法计算点云的占地面积

目录 一、算法原理二、代码实现三、结果展示四、测试数据本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT生成的文章。 一、算法原理 该方法主要用于粗略统计机载点云的占地面积。方法原理是将点云沿 X O Y XOY

Rust 常用集合(上)

目录 1、使用 Vector 储存列表 1.1 新建 vector 1.2 更新 vector 1.3 读取 vector 的元素 1.4 遍历 vector 中的元素 1.5 使用枚举来储存多种类型 1.6 丢弃 vector 时也会丢弃其所有元素 2、使用字符串储存 UTF-8 编码的文本 2.1 什么是字符串&#xff1f; 2.2 新建字…

tiktok云手机有用吗?用哪个好?

很多做独立站的跨境卖家都会搭配一些社媒平台给自己引流带货&#xff0c;比如说目前很火的TikTok&#xff0c;这也是目前比较有效的一种引流方式。本文将介绍tiktok运营方法以及如何用tiktok云手机规避运营风险。 TikTok是个不错的风口&#xff0c;不过我们在国内想要运营好Tik…

如何在CentOS安装SQL Server数据库并通过内网穿透工具实现公网访问

文章目录 前言1. 安装sql server2. 局域网测试连接3. 安装cpolar内网穿透4. 将sqlserver映射到公网5. 公网远程连接6.固定连接公网地址7.使用固定公网地址连接 前言 简单几步实现在Linux centos环境下安装部署sql server数据库&#xff0c;并结合cpolar内网穿透工具&#xff0…

第19课 在Android环境中使用FFmpeg和openCV进行开发的一般步骤

在上节课&#xff0c;根据模板文件我们对在Android环境中使用FFmpeg和openCV进行开发有了一个初步的体验&#xff0c;这节课&#xff0c;我们来具体看一下其工作流程。 1.程序的入口 与VS2013程序开发类似&#xff0c;Android程序开发也有一个入口&#xff0c;在这个模板中&a…

哈希-力扣202快乐数

题目 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终变不到 1。如果这个过程 结果为 1&…

lm358充电器应用电路

什么是LM358 LM358是双运算放大器。内部包括有两个独立的、高增益、内部频率补偿的运算放大器&#xff0c;适合于电源电压范围很宽的单电源使用&#xff0c;也适用于双电源工作模式&#xff0c;在推荐的工作条件下&#xff0c;电源电流与电源电压无关。它的使用范围包括传感放…

「PyMuPDF 专栏 」PyMuPDF创建PDF、拆分PDF

文章目录 一、本章前言二、使用PyMuPDF创建PDF文档1、实例代码2、过程详解①. 安装PyMuPDF②. 导入PyMuPDF模块③. 创建一个新的PDF文档④. 添加页面和内容⑤. 保存文档 三、使用PyMuPDF拆分PDF文档1、实例代码2、过程解析①. 导入模块②. 定义函数③. 打开源PDF文件④. 遍历页…

数字后端设计实现之自动化useful skew技术(Concurrent Clock Data)

在数字IC后端设计实现过程中&#xff0c;我们一直强调做时钟树综合要把clock skew做到最小。原因是clock skew的存在对整体设计的timing是不利的。 但是具体到某些timing path&#xff0c;可能它的local clock skew对timing是有帮助的&#xff0c;比如如下图所示。 第一级FF到第…

C++面试宝典第16题:盛最多水的容器

题目 给定n个非负整数a1、a2、…、an,每个数代表坐标中的一个点(i, ai)。画n条垂直线,使得第i条垂直线的两个端点分别为(i, ai)和(i, 0)。找出其中的两条线,使得它们与x轴共同构成的容器可以容纳最多的水。说明:不能倾斜容器,且n的取值至少为2。 在下图中,垂直线代表的输…

C++——冒泡排序

作用&#xff1a;最常用的排序算法&#xff0c;对数组内元素进行排序 1&#xff0c;比较相邻的元素&#xff0c;如果第一个比第二个大&#xff0c;就交换他们两个。 2&#xff0c;对每一对相邻元素做同样的工作&#xff0c;执行完毕后&#xff0c;找到第一个最大值。 3&…

RHCE9学习指南 第16章 访问NFS存储及自动挂载

16.1 访问NFS存储 前面介绍了使用本地存储&#xff0c;本章介绍使用网络上的存储设备。NFS全称是网络文件系统&#xff0c;所实现的是Linux和Linux之间的共享。 下面的练习我们将会在server上创建一个文件夹/share&#xff0c;然后通过NFS把它共享&#xff0c;然后在server2上…