C语言VS实用调试技巧

news2024/11/16 19:44:49

文章目录

  • 一、什么是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.coloradmin.cn/o/2172206.html

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

相关文章

外国名人面孔识别系统源码分享

外国名人面孔识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer…

【安当产品应用案例100集】018-Vmware Horizon如何通过安当ASP身份认证系统增强登录安全性

启用Radius认证是提高VMware Horizon环境安全性的有效方法&#xff0c;特别是在需要满足复杂安全要求的场景中。 启用Radius认证对于VMware Horizon具有以下几个关键优势&#xff1a; 增强安全性&#xff1a;Radius认证支持多种认证方法&#xff0c;包括PAP、CHAP、MS-CHAPv1…

Funsound 跨语言语音翻译

对任意语言的音视频识别得到目标语言文字结果&#xff0c;支持100种语言互译&#xff0c;视频支持在线添加字幕 体验地址 https://www.funsound.cn/st https://modelscope.cn/studios/QuadraV/Funsound_Multiligual_translator https://huggingface.co/spaces/QuadraV/Funsou…

基于nodejs的网球/篮球/体育场地管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

【芋道源码】gitee很火的开源项目pig——后台管理快速开发框架使用笔记(微服务版之本地开发环境篇)

后台管理快速开发框架使用笔记&#xff08;微服务版之本地开发环境篇&#xff09; 后台管理快速开发框架使用笔记&#xff08;微服务版之本地开发环境篇&#xff09; 后台管理快速开发框架使用笔记&#xff08;微服务版之本地开发环境篇&#xff09;前言一、如何获取项目&#…

HAL+M4学习记录_2

一、Boot配置 内存地址是固定的&#xff0c;代码从0x0000 0000开始&#xff0c;而数据从0x2000 0000开始&#xff0c;F4支持三种不同的boot模式 复位芯片时&#xff0c;在SYSCLK的第4个上升沿BOOT引脚值被锁存&#xff0c;STM32F407通过此时BOOT[1:0]引脚值选择Boot模式 BOOT1…

一种误差较小的计算轮廓法向的方法

1.前言 轮廓有正面和反面&#xff0c;可以通过其法向识别正反面&#xff0c;而法向是轮廓或面的重要特征&#xff0c;求轮廓法向是一种基础的几何工具算法。 由于浮点数存储和运算的精度损失&#xff0c;可能造成求轮廓法向的精度损失&#xff0c;如角点由于精度损失并非精确的…

工业5G路由器赋能防灾减灾地震监测物联网应用

在智慧城市及科技迅速发展的时代&#xff0c;地震监测作为防灾减灾的关键设施&#xff0c;正逐渐融入物联网技术的广阔蓝图中。工业物联网路由器作为关键的通讯枢纽&#xff0c;赋能地震监测实现智能化升级。 地震监测过程要求数据传输的实时性和准确性&#xff0c;而工业物联网…

DK5V100R15ST1直插TO220F,12V 4A两个引脚同步整流芯片

高性能两个引脚同步整流芯片 DK5V100R15ST1产品 概述DK5V100R15ST1是一款简单高效率的同步整流芯片&#xff0c;只有A&#xff0c;K两个引脚&#xff0c;分别对应肖特基二极管PN管脚。芯片内部集成了100V功率NMOS管&#xff0c;可以大幅降低二极管导通损耗&#xff0c;提高整机…

【计算机网络 - 基础问题】每日 3 题(二十五)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…

为什么电瓶车上楼充电引起的电气火灾还是一直发生?

0引言 近日&#xff0c;电瓶车火灾事件再次发生&#xff0c;随后主管部门、物业等相关方纷纷发布通知&#xff0c;提出种种禁令&#xff0c;然而这些措施似乎并未触及问题的核心&#xff0c;其效果也显得微乎其微。唯有从根本上解决问题&#xff0c;才能有效消除安全隐患。回顾…

项目实战:Ingress搭建Nginx+WP论坛+MariaDB

1. 网站架构 本次部署形式完全舍弃 Docker&#xff0c;将所有应用都置于Kubernetes&#xff0c;采用 Deployment 而非单 Pod 部署&#xff0c;稳定性得到升级。 2. 部署 MariaDB [rootk8s-master ~]# mkdir tdr [rootk8s-master ~]# cd tdr/ &#xff08;1&#xff09;定义 …

再也不怕数据丢失!四款数据恢复免费软件分享!

在数据满天飞的今天&#xff0c;一不小心就可能遭遇数据丢失的“惊魂一刻”。虽然数据丢失几乎是大家遇到过的事情&#xff0c;不仅仅是数据被删除&#xff0c;也可能是因为一些小意外。不过别担心&#xff0c;今天就来聊聊四位数据恢复界的“护法”&#xff0c;看看它们在实战…

PostgreSQL 创建表,常规表、外部表、分区表区别讲解

PostgreSQL 创建表&#xff0c;常规表、外部表、分区表区别讲解 创建表&#xff0c;常规表、外部表、分区表区一、常规表1. 定义和特点&#xff1a;2. 适用场景&#xff1a; 二、外部表1. 定义和特点&#xff1a;2. 适用场景&#xff1a; 三、分区表1. 定义和特点&#xff1a;2…

quiz: python网络爬虫之规则1

下面答错了&#xff1a; B c 8A&#xff0c; 9A

代码随想录算法训练营第四十三天 | 300.最长递增子序列,674. 最长连续递增序列,718. 最长重复子数组

四十三天打卡&#xff0c;今天解决子序列系列题目&#xff0c;定义dp[i]为以nums[i]为结尾的最长子序列长度。 300.最长递增子序列 题目链接 2024.7.12一刷 没做出来。这题答案使用dp做&#xff0c;答案也不是dp.back()&#xff0c;思路需要转换一下。 2024.9.27二刷 解题…

酒店智能门锁接口pro[V10] 对接酒店收银-SAAS本地化-未来之窗行业应用跨平台架构

一、文档 调用函数库&#xff1a; 提供Windows下的32位动态连接库proRFL.DLL&#xff0c;函数使用详细说明 //-----------------------------------------------------------------------------------// 功能&#xff1a;读DLL版本&#xff0c;不涉及USB口操作 C原型&…

Unity八股总结

这里写目录标题 OnEnable、Awake、Start运行时的发生顺序&#xff1f;哪些可能在同一个对象周期中反复的发生&#xff1f;动态加载资源的方式?Unity3d脚本从唤醒到销毁有着一套比较完整的生命周期&#xff0c;请列出系统自带的几个重要的方法。物理更新一般放在哪个系统函数里…

视频加字幕软件:5款好用加字幕软件来袭!

在视频制作过程中&#xff0c;添加字幕不仅能够提升观众的观看体验&#xff0c;还能有效传达视频信息&#xff0c;增强内容的吸引力和理解度。然而&#xff0c;市面上众多的视频加字幕软件让人眼花缭乱&#xff0c;不知如何选择。今天&#xff0c;我们就来盘点五款既实用又易上…

设计模式之享元(Flyweight)模式

前言 面向对象很好地解决了 “抽象” 的问题&#xff0c;但是不可避免的要付出一定的代价。对于通常情况来讲&#xff0c;面向对象的成本大都可以忽略不计。但是某些情况&#xff0c;面向对象所带来的成本必须谨慎处理 具体需要自己根据需求去评估 定义 “对象性能” 模式。运用…