C语言第二十课:实用调试技巧

news/2024/11/27 22:38:53/

目录

前言:

一、Bug:

二、调试:

        1.调试是什么:

        2.调试的基本步骤:

        3. Debug 与 Release :

三、在Windows环境下进行调试:

        1.调试环境的准备:

        2.调试的快捷键:

        3.调试查看程序当前信息:

        ①.查看临时变量的值:

        ②.查看内存信息:

        ③.查看调用堆栈:

        ④.查看汇编信息:

        ⑤.查看寄存器信息:

四、如何写出好的代码:

五、编程常见错误:

        1.编译型错误:

        2.链接型错误:

        3.运行时错误:

六、总结:


892bef7cd61f47feb732e807c0731636.jpeg

前言:

        在前面的十九篇文章中,我们较为系统的学习了C语言的各种语法结构,相信此时的各位小伙伴们已经有能力写出一些简单的C语言程序了。而今天我将要带领各位小伙伴们学习初阶C语言的最后一课——调试。并且今天的课程较为轻松,主要是为大家介绍一些实用的调试小技巧,能希望能对大家的代码查错和调有所帮助。

一、Bug:

        作为一名程序员,bug这个词应该是耳熟能详。相信你们在平时在身边小伙伴们的口中也一定常常听到这个词汇,那么bug到底是什么呢?它又是如何产生的呢?

        英文的 Bug,即臭虫,翻译成中文为程序缺陷,是指在软件运行中因为程序本身有错误而造成的功能不正常、死机、数据丢失、非正常中断等现象

        而程序错误之所以被称为“臭虫”,还要从一位老奶奶说起。这个人,就是与阿兰·图灵、史蒂夫·乔布斯、比尔·盖茨等一同入选“IT界十大最有远见的人才”的唯一一位女性——格蕾丝·赫柏

0daada3cb8d24d7d8e1e1412c86d4c56.webp

        1906年,赫柏出生在美国纽约。从上学起,赫柏在数学、物理方面都异常出色,一路顺顺利利直到16岁参加高考,却因为偏科太严重,拉丁文考试不及格,没能考上大学。复读一年后,赫柏考上韦莎学院。这所大学在2012年《福布斯》公布的美国最好大学中排名第20位,新闻报道更是将它评价为最值得选择的大学。毕业时,赫柏不仅同时获得数学、物理学位,还获得美国优等生的荣誉,留校担任教师的她被聘为学院副教授。赫柏的曾祖父是一名海军将军。1939年二战爆发时,满怀爱国热情的赫柏也坚决要求加入海军,毕业的赫柏因为出色的数学背景,被分配到美国船舶局位于哈佛大学的战时科研中心。彼时,军方正在开展世界第一台大型数字计算机的研究项目——马克一号。赫柏被任命为著名计算机专家霍德艾肯博士的助手,成为这个项目的第三名程序员。一天,计算机发生故障,赫柏经过排查,在计算机的继电器触电里,找到了一只被夹扁的小飞蛾,这只小虫子卡住了机器的运行,赫柏顺手将飞蛾夹在工作笔记里,并诙谐的把程序故障称为“bug”。这就是我们今天最爱说的“bug”的由来。它的意思,和原身一致,真就是“一只臭虫”。

ab9bb9c390c1464097cac683d1c0b03e.webp

        这一称呼后来演变成表达缺陷漏洞的计算机专业术语,人们习惯地把排除程序故障叫做“debug”(除虫)。

二、调试:

        一名优秀的程序员也是一名出色的侦探每一次调试都是一次破案的过程。所有要发生的事情都一定有迹可循,问心无愧就不存在痕迹,自然无需遮遮掩掩,倘若问心有愧,则必然遮掩一番,一旦遮掩就一定会留下痕迹,而留下的痕迹越多,就越容易顺藤而上,这就是推理的途径。顺着这条途径顺流而下就是犯罪,逆流而上,则是真相。

        那么我们是怎么写代码的呢?我相信很多小伙伴们在写代码时都是这样的

        可是编写代码一股脑闷着写就可以了吗?答案自然是不行。这样不管不顾的去编写代码,最后一般会产生两个经典问题这程序为什么跑不起来?这程序又是怎么跑起来的呢?

acd8ed41f0d74b738b0df0d5b5e9a6a4.jpeg

        当遇到这样的问题的时候,就需要我们对程序进行调试,来验证和查看我们程序的每一个运行细节

        1.调试是什么:

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

        调试是一个多步骤的过程,包括识别问题隔离问题源,然后纠正问题或确定解决问题的方法。在软件开发中,调试涉及定位纠正计算机程序中的代码错误。调试过程在编写代码后立即开始,并随着代码与其他编程单元组合形成软件产品而在连续阶段继续进行。通过使用单元测试代码审查结对编程等策略,可以使调试过程更容易。

        2.调试的基本步骤:

        · 发现程序错误的存在

        · 以隔离、消除等方式对存物进行定位

        · 确定错误产生的成因

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

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

        3. Debug 与 Release :

        我们的 VS 2022 内置有两种编译版本,分别是 Debug 版本Release 版本,两种版本村踩着一定的差异。

        Debug 版本通常称为调试版本,它包含调试信息,并且不做任何优化便于程序员调试程序。

        Release 版本通常则被称为发布版本,它往往会对我们的代码进行各种优化使得程序在代码大小和运行速度上都是最优的以便用户很好的使用

        这两种版本在很多地方都存在着差异,我们以下面这段代码为例:

#define _CRT_SECURE_NO_WARNINGS 1#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;
}

        为了进行演示,我们故意写出了一个bug程序,即在进行数组元素访问时,使其越界访问

        首先我们来看本地文件的差别(上为 Debug 版本,下为 Release 版本):

0b0af7e140ad44919a6a178d88ff8e17.png

be4f52795afd46c0a0e5b81602ed2944.png

        我们可以清楚的看到,上面的 Debug 模式没有对我们编写的程序进行优化,存放在本地的 exe 文件大小为40KB;而下面的 Release 模式对我们的代码进行了优化,使其缩小为仅仅7KB。这仅仅是一个只有10行不到的代码,就会产生近六倍的差距,我们可以大胆想象一下如果我们的有上百万行,那么程序大小的差异会有多大。

        接着我们来看实际的程序运行结果(上为 Debug 版本,下为 Release 版本):

b03b38e9e4de400da9ac5b0bb4f203e3.png

a240afc626ea4f1dbebff783aaf83c55.png

        我们可以看到,Debug 版本没有优化我们的程序,导致我们写出的 bug 代码原封不动的被执行,在越界访问时造成了程序的死循环;而同样的代码经过 Release 版本的各种优化,我们看到我们的 bug 程序在运行时没有陷入死循环,而是被及时的制止了。 

        最后我们来看看两种版本下的反汇编代码的差异(上为 Debug 版本,下为 Release 版本):

fc213c8b33534bc3aa71047f0c72c93a.png

3214f607f9ca4d9a927509b8013eb6e0.png

        我们可以看到,反汇编代码清晰的展示了两种不同版本下执行的空间、时间效率的差异,显而易见的,Release 版本在优化上远远优于 Debug 版本

        那么既然 Release 版本这么好,我们为什么还要介绍 Debug 版本呢?甚至于说,后文主要就是针对 Debug 版本进行的讲解呢?原因是,★ Debug 模式支持调试而 Release 版本不支持 ★

三、在Windows环境下进行调试:

        1.调试环境的准备:

        首先我们要在环境中选择 Debug 版本,才能使代码正常调试:

e9c240182fe54fa3a97a94e3aab2aaec.png

        2.调试的快捷键:

        在我们的调试过程中,需要用到许多调试功能

d65357bfcf3d412ab8b10bf0eb096fed.jpeg

        为了便于我们更高效的调试,我们有必要记住这些常用的调试快捷键

F5启动调试,常用来跳至下一断点处。

F9创建断点与取消断点。断点的作用为,可以在任意程序语句处打上断点,可以使程序在运行至断点处后暂停执行,便于我们接下来一步一步地执行,方便我们对程序细节进行观查。

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

F11逐语句。即每次执行一条语句,但是这个快捷键可以使我们的执行逻辑深入到函数内部去(最常用)。

CTRL+F5开始执行而不调试。如果你型让你的程序直接运行起来而不进行调试就可以直接使用。

        3.调试查看程序当前信息:

        ①.查看临时变量的值:

        我们通过 调试 => 窗口 => 监视 ,可以打开监视窗口,在窗口内输入你想要查看的变量名,即可在监视窗口内查看到该临时变量在程序运行时产生的实时变化了(调试状态下):

d11ff952e8ad47de85e2bec555fbd7ce.png

        ②.查看内存信息:

        同样的我们通过 调试 => 窗口 => 内存,可以打开内存窗口,在窗口内输入你想要查看的变量的地址(或取地址操作符+变量名),即可在内存窗口内查看到目标对象的内存使用在程序运行时产生的实时变化了(调试状态下):

0d640f5c55d34d1b845c1b07c3877c77.png

        ③.查看调用堆栈:

        也还是一样,我们通过 调试 => 窗口 => 调用堆栈,可以打开调用堆栈窗口,不同的是该窗口无需输入,会按照当前正在执行的语句位置,反馈出当前程序的堆栈调用情况调试状态下):

6e961633b02a44c3a0e17de5aa5f1870.png

        ④.查看汇编信息:

        我们一般有两种方式去查看汇编信息,第一种是在调试状态下,与上面相同的方式,通过 调试 => 窗口=> 反汇编,打开汇编窗口;而第二种方式是在调试状态下,直接单击鼠标右键,选择转到反汇编,即可打开汇编窗口。这两种方式均可以打开汇编代码窗口,来查看我们写出的代码对应的汇编代码调试状态下):

6fa16dbefade40be9a6b9a0b2b37b348.png

        ⑤.查看寄存器信息:

        通过 调试 => 窗口 => 寄存器,打开寄存器窗口,可以查看当前运行环境的寄存器使用信息调试状态下):

46e93da90035484db92aaf7ee638ac1d.png

四、如何写出好的代码:

        好的代码通常都是易于调试的。优秀的代码通常都具有运行正常bug少效率高可读性高可维护性高注释清晰文档齐全等优点。

        而常用的 coding 技巧主要有使用assert尽可能使用const、使用好的代码注释风格添加必要的代码注释避免编码陷阱等等,再者就取决于程序员自己的亲身代码编写经验了。

        同时各位小伙伴们在编写代码时应当注意,我们在编写程序代码时,要仔细认真的分析参数以及返回值类型的设计尽最大可能避免空指针、野指针对我们程序运行造成的危害等等容易出现错误的地方。再者也就是及时的为我们的代码添加上合适的注释,既便于我们在调试阶段检查我们代码的逻辑性错误,也能大大提高我们代码的可读性与可维护性

        在这里再送给各位小伙伴们一张 “ 没有BUG符 ” ,祝愿各位小伙伴们今后在编写程序代码时:一看就会一写就对没有BUG令人陶醉!

1bc32580d9d64b26bb3c0fa03e6e59be.jpeg

五、编程常见错误:

        1.编译型错误:

        这种类型的错误最为常见,我们可以直接通过查看错误信息提示(双击)解决问题,或凭借我们的代码编写经验,也很容易就能解决,相对来说较为简单。

        2.链接型错误:

        这种类型的错误要稍微难处理一点,我们的处理方法主要是通过查看错误提示信息,在我们的代码中找到错误信息种的标识符,然后逐步调试定位问题所在。这类问题多出现于标识符名、不存在、不符和或存在拼写错误。

        3.运行时错误:

        这类问题最为麻烦,因为它们既在编写语法上没有问题,也不存在拼写错误,而一般常见于我们所编写出来的程序的逻辑漏洞。即看上去没有问题,但在执行时得不出我们想要的执行结果。面对这类问题,我们只能通过借助调试逐步定位问题,慢慢解决。

六、总结:

        到这里,我们关于初阶C语言的学习就全部结束了。在这里我非常感谢各位小伙伴们这么久以来对我的鼓励与支持,每次写到想要放弃时总会有小伙伴私信我为我加油鼓劲。如今我们的学习之旅暂时是告一段落了,不过学习之路还远远没有完结。接下来我会休息几天,调整调整状态,便会开始下一阶段的学习讲解,希望到时候还能看到各位小伙伴们熟的身影。

        希望这些日子写出的一些文字能对各位小伙伴们有所帮助,用心了它们是知识、是力量,不够用心那它们也不过是一堆冰冷的文字罢了。成功是陡峭的阶梯,两手插在裤袋里是爬不上去的,当你想要放弃的时候想想当初为什么坚持到这里!

        新人初来乍到,辛苦各位小伙伴们动动小手,三连走一走 ~ ~ ~  最后,本文仍有许多不足之处,欢迎各位看官老爷随时私信批评指正!


http://www.ppmy.cn/news/2631.html

相关文章

javascript基础小结(一)

今天突发奇想&#xff0c;想要垂直精学一段时间的javascript&#xff0c;用我的第一次「连载」来记录总结一些知识点吧。 知识点 原始类型的类型转换 类型转换 alert 会自动将任何值都转换为字符串以进行显示。算术运算符会将值转换为数字常用的类型转换&#xff1a;转换为 …

Privacy

For information collected and further processed under this Privacy Policy, the data controller is Toy Games. Toy Games Ltd (“Toy Games”, “owner”, “us”, “our” or “we”) is dedicated to protecting the privacy rights of our games and other services …

C语言——内存中数据存储的详解(整型与浮点型)

文章目录1.数据类型的详细介绍1.1数据类型介绍1.2类型的基本归类整型类型浮点类型构造类型指针类型空类型2.整型在内存中的存储2.1原码、反码、补码2.2大小端存储大小端存储存在的意义编写一个程序判断当前机器的存储方式是大端存储还是小端存储2.3经典习题练习一练习二练习三c…

ucore的makefile学习-以实际例子来展示

使用lab1_result目录 执行的命令为 make mytest代码均添加在 listf include tools/function.mk后面一行。 mytest:echo $(call listf,./libs,c)结果 ./libs/string.c ./libs/printfmt.c toobj mytest:echo $(call toobj,a.c,hello)输出 obj/hello/a.o todep 跟toobj相似…

Python环境搭建

将向大家介绍如何在本地搭建Python开发环境。 Python可应用于多平台包括 Linux 和 Mac OS X。 你可以通过终端窗口输入 "python" 命令来查看本地是否已经安装Python以及Python的安装版本。 Unix (Solaris, Linux, FreeBSD, AIX, HP/UX, SunOS, IRIX, 等等。) W…

Android实现车辆检测(含Android源码 可实时运行)

Android实现车辆检测(含Android源码 可实时运行) 目录 Android实现车辆检测(含Android源码 可实时运行) 1. 前言 2. 车辆检测数据集说明 3. 基于YOLOv5的车辆检测模型训练 4.车辆检测模型Android部署 &#xff08;1&#xff09; 将Pytorch模型转换ONNX模型 &#xff08…

Python 常见单词-集合

为了方便大家更好的入门 Python 学习&#xff0c;已经整理好了 Python 语言入门常见 的英文单词&#xff0c;词汇量不大&#xff0c;大概百十来个&#xff0c;多敲多练&#xff0c;预估两周左右可以熟记&#xff01;大家 加油噢~ 一、交互式环境与 print 输出 1、print&#x…

【VC7升级VC8】将vCenter Server 7.X 升级为 vCenter Server 8 (下)—— 升级步骤说明

目录前文说明3. 第一阶段升级&#xff08;1&#xff09;点击【升级】&#xff08;2&#xff09;升级介绍&#xff08;3&#xff09;最终中用户许可协议&#xff08;4&#xff09;连接到源设备&#xff08;5&#xff09;VC7与ESXi 证书警告&#xff08;6&#xff09;vCenter Ser…