C语言VS实用调试技巧

文章目录

  • 一、什么是bug?
  • 二、什么是调试?
  • 三、Debug和Release
  • 四、VS调试快捷键
    • 4.1环境准备
    • 4.2调试快捷键
  • 五、监视和内存观察
    • 5.1监视
    • 5.2内存
  • 六、调试举例
  • 七、编程常见错误归类
    • 7.1编译型错误
    • 7.2链接型错误
    • 7.3运行时错误

一、什么是bug?

🍎bug本意是 “昆虫” 或 “虫子”,现在一般是指在电脑系统或程序中,隐藏着的一些未被发现的缺陷或问题,简称程序漏洞

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

二、什么是调试?

🍋当我们发现程序中存在问题的时候,那下一步就是找到问题,并修复问题。
🍋这个找问题的过程被称为调试,英文叫debug(消灭bug的意思)。
🍋调试一个程序,首先得承认出现了问题,然后通过各种手段去定位问题的位置,可能是逐过程的调试,也可能是隔离和屏蔽代码的方式,找到问题所在的位置,然后确定错误产生的原因,再修复代码重新测试。

三、Debug和Release

在这里插入图片描述在VS上编写代码的时候,就能看到有 debug 和 release 两个选项,分别是什么意思呢?

Debug通常称为调试版本式,它包含调试信息,并且不作任何优化,便于程序员调试程序;程序员在写代码的时候,需要经常性的调试代码,就将这里设置为 debug,这样编译产生的是debug版本的可执行程序,其中包含调试信息,是可以直接调试的。
Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。当程序员写完代码之后,再对程序进行测试,直到程序的质量符合交付给用户使用的标准,这个时候就会设置为 release ,编译产生的就是release版本的可执行程序,这个版本是用户使用的,无需包含调试信息。

如果在两个版本下分别调试上面图片中的代码:就会得到Debug和Release两个版本下的可执行程序:(运行程序后,在下面的输出窗口会告诉我们生成的可执行程序(.exe)的文件位置)
在这里插入图片描述在这里插入图片描述在这里插入图片描述可以看到同样的程序在两个版本下生成的可执行程序的大小是有差距的,相比较于Debug版本的可执行程序,Release版本的更小。

四、VS调试快捷键

那程序员怎么调试代码呢?

4.1环境准备

首先是环境的准备,需要一个支持调试的开发环境,我使用的编译器是VS2022,应该把VS上的环境设置为Debug,如图:
在这里插入图片描述

4.2调试快捷键

调试最常使用的几个快捷键
🍅F9创建断点和取消断点
断点的作用是可以在程序的任意位置设置断点,打上断点就可以使得程序执行到想要的位置暂定执行,接下来我们就可以使用F10F11这些快捷键,观察代码的执行细节。
条件断点满足这个条件,才触发断点
🍅F5启动调试,经常用来直接跳到下一个断点处,一般是和F9配合使用。
🍅F10逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句。
🍅F11逐语句,就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数的内部。在函数调用的地方,想进入函数观察细节,必须使用F11,如果使用F10,会直接完成函数调用。
🍅Ctrl+F5开始执行不调试,如果你想让程序直接运行起来而不是调试,就可以直接使用Ctrl+F5
VS更多快捷键了解:http://blog.csdn.net/mrlisky/article/details/72622009

(1) F9设置的断点如下图,光标在第13行的语句处按一下F9就会出现一个红色的点,就是断点:(再按一下F9取消断点)
在这里插入图片描述这个时候,再按F5调试起来后,程序就会直接来到断点处,在监视窗口里可以看到,在断点前面的程序其实已经执行了,然后程序就停在断点处。
在这里插入图片描述在这里插入图片描述

如果一个程序中设置有多个断点呢?那按F5会执行到哪呢?
在这里插入图片描述在这里插入图片描述可以看到如果程序中有多个断点时,那第一次按F5会先执行到最前面的断点处,此时在监视窗口可以看到变量i的值是0,如果在第一次按F5的基础上,再按一次F5,变量i的值变为:
在这里插入图片描述在第二次按F5的基础上,再按第三次F5,i的值变为:
在这里插入图片描述再在监视窗口查看arr数组:
在这里插入图片描述通过上面的测试我们可以看出,在设置断点后,再配合使用F5,那程序停顿的断点处其实是一个逻辑断点,意思是第一次执行到断点处,比如上面第9行的断点。第一次按F5调试起来,程序会停在这一行,断点前面的程序其实已经运行了,然后停在断点所在的语句行,当第二次按F5调试后,因为是一个for循环,所以在执行了arr[i]=i这条语句后,会返回上到上面的循环调整部分,也就是i++部分,然后来到循环条件的判断部分,再然后就第二次遇到断点,又停在断点处等待下一次的F5调试。所以为什么称之为逻辑断点就是这个意思。(注意:当F5执行到断点所在的行时只是停在这一行,但这一行的语句是不运行的)

(2) 条件断点:就拿上面的程序来说明什么是条件断点:在第9行设置条件断点,如下图鼠标放在断点上,单击鼠标右键:
在这里插入图片描述在这里插入图片描述条件断点就是当满足某种条件时(才会触发这个断点),程序才会停在断点处。上面设置了当i==5时,程序才会停顿在第9行:
在这里插入图片描述满足条件的断点,不是说程序只能执行到断点处而不能再调试啦。就像上面,如果第二次按F5调试,由于i在等于5后,for循环会使i往上递增,i==5的条件就失效了,所以第二次按F5程序就一次性运行完了。

(3) F10是逐过程的执行语句(也就是一条语句一条语句的执行),但是当遇到调用函数时,不会进入到函数的内部,而是直接调用完函数。F10也可以配合F9、F5一起使用,如果在程序中发现有一段代码出现了bug,那可以在该处设置断点,然后再按F10逐语句的进行调试找问题。
在这里插入图片描述由上图,如果确定是第二个for循环部分出现了bug,那在13行设置断点,之后先按F5调试停在断点处,接下来就按F10一步一步的执行来找bug。这样做的好处是,如果一开始就按F10,一个语句一个语句的执行,由于第一个for循环的循环条件部分太长了,那只按F10来执行程序就太费时了。而设置了断点后,F5调试起来就可以直接来到第二个for循环处,并且第二个for循环的循环条件部分很短。所以这就是F9、F5、F10配合使用的好处,当程序的语句(逻辑上)很短时,用F10执行就很好。

(4)F11与F10的区别就是:在遇到函数调用的时候,按F11能够进到函数内部,执行函数内部的每条语句,这样就能更细致地观察函数内部的代码是怎么运行的。所以比起F10,F11其实能更细致入微地执行程序中的语句。

比如我们定义了一个test函数,在main函数中调用test函数,我们先在调用test函数的地方设置一个断点,然后按F5调试起来后,来到test函数处,我们再按F11进入函数内部,进入函数内部后就可以按F10或者F11来执行语句都可以:
在这里插入图片描述当然还有一个ctrl+F5快捷键,意思是开始执行但不调试。ctrl+F5和F5是有区别的:如果按ctrl+F5执行程序,那不管程序中有没有设置断点程序都会一次性运行完。所以小伙伴们要熟练掌握这些调试快捷键,学会了这些调试快捷键就可以更好的调试程序,找程序中的bug。(注意:在笔记本上电脑上,如果单独按这些快捷键不起作用,那就按Fn+这些快捷键)

五、监视和内存观察

在调试的过程中,如果想观察代码执行过程中,上下文环境中的变量的值,有哪些方法呢? 这些观察的前提条件一定是开始调试后才能观察,比如按F10调试下面的代码:

#include<stdio.h>
int main()
{int arr[10] = { 0 };int num = 100;char c = 'w';int i = 0;for (i = 0; i < 10; i++){arr[i] = i;}return 0;
}

5.1监视

上面的代码按F10调试起来,就可以在菜单栏中的【调试】->【窗口】->【监视】,打开任意一个监视窗口,输入想要观察的对象即可:
在这里插入图片描述在这里插入图片描述监视窗口如下:(按F10调试完程序后的变量i和数组arr的值)
在这里插入图片描述

5.2内存

如果在监视窗口看上面代码中的变量的值看得不够仔细,也可以在内存中观察变量的存储情况。在【调试】->【窗口】->【内存】中,任意选择一个内存窗口:
在这里插入图片描述在这里插入图片描述在内存窗口观察数据:(边按F10边调试看内存中变量的值的存储情况)
在这里插入图片描述在这里插入图片描述在打开内存窗口后,要在地址栏输入:arr(数组名),&num,&c,这类地址,就能观察到该地址处的数据。如下图,当F10调试到i==9的时候的内存和监视:
在这里插入图片描述在这里插入图片描述除此之外,在调试的窗口中还有:自动窗口,局部变量,反汇编、寄存器等窗口,请自行验证使用。

六、调试举例

在VS2022、X86、Debug的环境下,编译器不做任何优化的话,下面代码执行的结果是啥?

#include<stdio.h>
int main()
{int i = 0;int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };for (i = 0; i <= 12; i++){arr[i] = 0;printf("hehe\n");}return 0;
}

运行的结果是死循环的打印"hehe",原因是什么呢?
可以在调试中,从内存里面查看数组arr和变量i的存储情况:

在这里插入图片描述
在这里插入图片描述当F10调试到for循环的部分,进入循环就会把数组arr中的元素全部改成0。但是你注意到数组的元素个数只有10个,而for循环已经越界使用内存中的空间啦,当for循环的循环变量i超过9增加到12时,从上面的内存中可以看到其实已经访问到变量i的空间了,当arr[12]=0执行完毕,其实就是将变量i的值改为了0。这样就让变量i就又回到了0,又从0开始往上遍历。所以造成了死循环。

那为什么会出现这种情况呢?为什么变量i在内存中的地址就刚好在数组arr的后面(高地址处)?解释一下:
🥥1.栈区内存的使用习惯是从高地址向低地址使用的,所以变量i的地址是较大的。arr数组的地址整体是小于i的地址的。
🥥2.数组在内存中的存放是:随着下标的增长,地址是由低到高变化的。
因为是先创建的变量i,然后才创建的arr数组,所以在栈区中变量i的地址相较于数组arr是高地址。所以是这种内存布局,那随着数组下标的增长,往后越界就有可能覆盖到i,这样就可能造成死循环。

在这里插入图片描述至于为什么变量i和arr数组之间恰好会空出来2个整型的空间? 这里确实是巧合,在不同的编译器下可能中间空出的空间大小是不一样的,代码中这些变量的内存分配和地址分配是编译器指定的,所以不同的编译器之间就有差异了。这个题目是和环境相关的。

从这个例子中我们能够体会到调试的重要性,只有调试才能观察到程序内部执行的细节,就像医生给病人做B超、CT一样。
🥑注意:栈区的默认使用习惯是先使用高地址,再使用低地址的空间,但是这个具体还是要看编译器的实现。比如:在VS上切换到X64,内存空间的使用顺序就是相反的,在Release版本的程序中,这个使用的顺序也是相反的。

七、编程常见错误归类

7.1编译型错误

编译型错误一般都是语法错误,这类错误一般看错误信息就能找到一些蛛丝马迹,双击错误信息也能初步的跳转到代码错误的地方或者附近。编译型错误,随着语言的熟练掌握,会越来越少,也容易解决。
在这里插入图片描述

7.2链接型错误

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

在这里插入图片描述

7.3运行时错误

运行时错误,是千变万化的,需要借助调试,逐步定位问题,调试解决的是运行时问题。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端——切换轮播图

学完前端js小知识后&#xff0c;动手操作的一个简单图片轮播图。 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"keywords" content"关键词信息"><meta name"des…

MATLAB绘图基础9:多变量图形绘制

参考书&#xff1a;《 M A T L A B {\rm MATLAB} MATLAB与学术图表绘制》(关东升)。 9.多变量图形绘制 9.1 气泡图 气泡图用于展示三个或更多变量变量之间的关系&#xff0c;气泡图的组成要素&#xff1a; 横轴( X {\rm X} X轴)&#xff1a;表示数据集中的一个变量&#xff0c…

带徒实训项目实战讲义分享:ApiFirst文档对比功能页面开发2

前一篇&#xff1a;带徒实训项目实战讲义分享&#xff1a;ApiFirst文档对比功能页面开发 亲爱的学员朋友们好&#xff0c;本小节跟小卷一起来学习用thymeleaf模板技术来渲染数据模型到表格中&#xff0c;通过本小节的学习&#xff0c;你会真正将thymeleaf模板技术应用到实处&a…

Qt获取本机Mac地址、Ip地址

一、简述 今天给大家分享一个获取本机IP地址和Mac地址的方法&#xff0c;经过多次测试&#xff0c;台式机、笔记本等多个设备&#xff0c;暂时没有发现问题。 由于很多时候本地安装了虚拟机、蓝牙、无线网卡或者其他设备等&#xff0c;会有多个Mac地址&#xff0c;所以需要进…

汽车信息安全 -- 存到HSM中的密钥还需包裹吗?

目录 1.车规芯片的ROM_KEY 2.密钥加密与包裹 3.瑞萨RZ\T2M的密钥导入 4.小结 在车控类ECU中&#xff0c;我们通常把主控芯片MCU中的HSM以及HSM固件统一看做整个系统安全架构的信任根。 所以大家默认在HSM内部存储的数据等都是可信的&#xff0c;例如CycurHSM方案中使用HSM…

ControlGAN:Controllable Text-to-Image Generation

1 研究目的 当前的生成网络通常是不可控的&#xff0c;这意味着如果用户更改句子的某些单词&#xff0c;合成图像将与原始文本生成的合成图像显着不同&#xff1b;当给定的文本描述&#xff08;例如颜色&#xff09;发生变化时&#xff0c;鸟类的相应视觉属性被修改&#xff0c…

easyexcel常见问题分析

文章目录 一、读取数字多了很多小数位的精度问题 一、读取数字多了很多小数位的精度问题 浮点型转成BigDecimal的时候会出现精度问题&#xff0c;例如 这儿设置的实体类对象类型是String&#xff0c;默认用到的是StringNumberConverter转换器 2.1.4 版本 public class Strin…

【无人机设计与技术】四旋翼无人机的建模

摘要 本项目的目标是通过 Simulink 建模和仿真&#xff0c;研究四旋翼无人机的建模、姿态控制、定点位置控制及航点规划功能。无人机建模包含了动力单元模型、控制效率模型和刚体模型&#xff0c;并运用这些模型实现了姿态控制和位置控制。姿态控制为无人机的平稳飞行提供基础…

计算机视觉实战项目4(图像分类+目标检测+目标跟踪+姿态识别+车道线识别+车牌识别+无人机检测+A*路径规划+单目测距与测速+行人车辆计数等)

往期热门项目回顾&#xff1a; 计算机视觉项目大集合 改进的yolo目标检测-测距测速 路径规划算法 图像去雨去雾目标检测测距项目 交通标志识别项目 yolo系列-重磅yolov9界面-最新的yolo 姿态识别-3d姿态识别 深度学习小白学习路线 AI健身教练-引体向上-俯卧撑计数…

在VMware虚拟机上部署polardb

免密登录到我们的虚拟机之后&#xff0c;要在虚拟机上部署polardb数据库&#xff0c;首先第一步要先克隆源码&#xff1a; 为了进SSH协议进行传输源码需要先进行下面的步骤&#xff1a; 将宿主机上的私钥文件复制到虚拟机上 scp "C:\Users\waitw\.ssh\id_rsa" ann…

ThinkPHP发送邮件教程:从配置到发送指南!

ThinkPHP发送邮件功能实现策略&#xff1f;Thinkphp如何发邮件&#xff1f; ThinkPHP作为一个流行的PHP框架&#xff0c;提供了强大的邮件发送功能&#xff0c;使得开发者可以轻松地在应用中集成邮件发送功能。AokSend将详细介绍如何在ThinkPHP中配置和发送邮件。 ThinkPHP发…

【Linux-基础IO】如何理解Linux下一切皆文件磁盘的介绍

目录 如何理解Linux系统上一切皆文件 1.物理角度认识磁盘 2.对磁盘的存储进行逻辑抽象 磁盘寻址 3.磁盘中的寄存器 如何理解Linux系统上一切皆文件 计算机中包含大量外设&#xff0c;操作系统想要管理好这些外设&#xff0c;就必须对这些外设进行先描述再组织&#xff0c…

【Linux 23】线程池

文章目录 &#x1f308; 一、线程池的概念&#x1f308; 二、线程池的应用场景&#x1f308; 三、线程池的实现 &#x1f308; 一、线程池的概念 线程池 (thread pool) 是一种利用池化技术的线程使用模式。 虽然创建线程的代价比创建进程的要小很多&#xff0c;但小并不意味着…

一篇文章快速学会docker容器技术

目录 一、Docker简介及部署方法 1.1Docker简介 1.1.1什么是docker 1.1.2 docker在企业中的应用场景 1.1.3 docker与虚拟化的对比 1.1.4 docker的优势 二 、部署docker 2.1 容器工作方法 2.2 部署第一个容器 2.2.1 配置软件仓库 2.2.2 安装docker-ce并启动服务 2.2.…

【AIGC】ChatGPT提示词解析:如何生成爆款标题、节日热点文案与完美文字排版

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;情绪化的吸睛爆款标题提示词使用方法 &#x1f4af;紧跟节日热点选题文案提示词使用方法 &#x1f4af;高效文字排版技巧提示词使用方法 &#x1f4af;小结 &#x1f4af…

数据结构-链表笔记

移除节点 203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListN…

Oracle数据库体系结构基础

关于Oracle体系结构 基于Oracle11g体系结构 目标&#xff1a; 了解Oracle体系结构掌握逻辑存储结构掌握物理存储结构熟悉Oracle服务器结构熟悉常用的数据字典 Oracle数据库管理中的重要的三个概念 实例&#xff08;instance):实例是指一组Oracle后台进程以及在服务器中分配…

安宝特分享 | AR技术重塑工业:数字孪生与沉浸式培训的创新应用

在数字化转型的浪潮中&#xff0c;AR&#xff08;增强现实&#xff09;技术与工业的结合正在呈现新的趋势和应用延伸。特别是“数字孪生”概念的崛起&#xff0c;为AR技术在工业中提供了独特而创新的切入点。 本文将探索AR如何与数字孪生、沉浸式体验和实用案例相结合&#xf…

ResNet50V2:口腔癌分类

本文为为&#x1f517;365天深度学习训练营内部文章 原作者&#xff1a;K同学啊 一 ResNet和ResNetV2对比 改进点&#xff1a;(a)original表示原始的ResNet的残差结构&#xff0c;(b)proposed表示新的ResNet的残差结构&#xff0c;主要差别就是(a)结构先卷积后进行BN和激活函数…

JAVAEE如何实现网页(jsp)间的数据传输?一文总结

刚刚接触到JAVAEE的WEB开发&#xff0c;解释不周的地方希望感谢指正&#xff01;&#xff01;&#xff01; 情景如下&#xff1a; 我的使用是21版的IDEA&#xff0c;9.03版本的tomcat&#xff0c;来做一个示范。 构建项目 点击下一步 -> 完成&#xff0c;等待项目构建结束…