【C/调试实用技巧】—作为程序员应如何面对并尝试解决Bug?

news2024/11/20 2:46:00

小菜坤日常上传gitee代码:https://gitee.com/qi-dunyan
关于C语言的所有由浅入深的知识,都存放在专栏【c】
❤❤❤
个人简介:双一流非科班的一名小白,期待与各位大佬一起努力!
推荐网站:cplusplus.com
在这里插入图片描述

目录

  • 前言
    • BUG的前世今生
    • 程序员应如何对待BUG
  • 调试前言
    • 调试步骤
    • Debug和Release的介绍
  • 调试实战
    • 快捷键以及程序信息的查看
    • 遇到的常见错误类型介绍
    • 如何写出好的代码
    • assert 与 const(C语言)

前言

我相信大家在写代码,或者刷题时,不可能每一次都是一次就能写出完美的不出错误的代码,如果真实这样的话,恭喜你,你是一个天才,并不需要进行本篇文章的学习,此文章是我整理的本人作为小白时期遇到的一些BUG,以及遇到这些BUG应如何去解决它,文章适用于新入门的小白,对于CV老司机就不适用啦。(🐔鸡哥护体)
通过本章的学习,将会帮助小白能自己调试,并解决一些简单问题!

BUG的前世今生

首先要解决它,就要先知道它知己知彼百战不殆,BUG是一个英语单词,本意是昆虫的意思在这里插入图片描述
那么为何我们在写代码时,把错误称为BUG呢?这还真与昆虫有关。接下来大家可以看这么一个小故事。
1944年世界上第一台计算机马克1号诞生,之后在世界上第一位女程序员格蕾丝-霍普接手下,顺利改造成马克二号。
1946年的一天,霍普敲代码的时候发现计算机发生了故障,就在马克二号的继电器触点里,找到了一只被夹扁的小飞蛾。
而这只小虫子卡住了机器的运行,霍普顺手将飞蛾夹在工作笔记里,而备注的意思是臭虫,正是这一奇怪的称呼,奠定了Bug这个词在计算机世界的地位,此后,我们的生活便是处处与Bug打交道。
在这里插入图片描述

程序员应如何对待BUG

作为程序员的我们,应如何看待BUG呢?
在这里插入图片描述
在这里我们拒绝CV!调试的过程也是锻炼自己能力的一个过程!接下来将讲解有关调试的问题。

调试前言

调试,其实就是发现错误并修改减少错误的一个过程,就像画画一样,所谓画画,其实就是进行不断地修改,使画面更加和谐完美(美术生深有体会)最终成就一副完美的画面,调试也是如此,当代码遇到问题时,只有通过调试才能发现问题,并且解决问题!

调试步骤

调试的过程其实并不复杂,总结起来就以下几步,但做起来有时候确实要花费一些心思。后面会通过几个具体实例来讲解调试分析的具体步骤。

  1. 发现程序错误的存在
  2. 以隔离、消除等方式对错误进行定位
  3. 确定错误产生的原因
  4. 提出纠正错误的解决办法
  5. 对程序错误予以改正,重新测试

Debug和Release的介绍

Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。我们进行的调试,都是在此版本进行的。

Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。

在这里我们举个例子,以VS2019里的一个简单代码为例:

#include<stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	int c = a + b;
	printf("%d\n", c);

	return 0;
}

这是在Debug版本下,该代码所对应的反汇编
在这里插入图片描述
下面这是在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版本下,是进行死循环的打印,但是在Release版本下,却能打印出13个hehe,(后面会通过调试实战讲到具体的死循环原因,在这里只需要知道,Release版本下,会对代码进行优化即可)。
在这里插入图片描述

调试实战

快捷键以及程序信息的查看

接下来先介绍几个最常使用的快捷键:

F5
启动调试,经常用来直接跳到下一个断点处。
F9
创建断点和取消断点
断点的重要作用,可以在程序的任意位置设置断点。
这样就可以使得程序在想要的位置随意停止执行,继而一步步执行下去。
F10
逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句。
F11
逐语句,就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部(这是最长用的)。
CTRL + F5
开始执行不调试,如果你想让程序直接运行起来而不调试就可以直接使用。

我们在调试的时候,当然不仅仅是只看这两个快捷键,而是要通过查看程序的当前信息配合着使用,才能发挥最大作用。(Debug版本下进行调试)

这里以上面的代码为例:解释下面代码在vs中运行死循环的原因。

#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;
}

1.0 监视
首先是监视,它可以使我们在调试的时候清楚的知道变量随之的变化,从而进行判断所出的问题,并及时做出相应解决方案。
一定要在调试的环境下(按F10进行逐过程,或者F9+F5打断点调试),再去点击调试—窗口—监视
在这里插入图片描述

然后再监视窗口里,添加观察对象(变量),然后根据自己需要,进行调试,这里我进行逐语句调试,观察变量的变化。为什么会在Debug版本下死循环打印呢?

在这里插入图片描述
我们发现,当i==12时,i又一次变成了0,所以才进行死循环的打印。看来问题出在这里了(后面会讲到具体原因)
在这里插入图片描述
2.0 查看内存信息
在进入调试时,我们可以观察这些变量的内存信息,了解它们是如何在内存中存储的,有助于帮助我们进一步解决问题。
调试—窗口—内存
在这里插入图片描述
在这里为了便于观察,我们单机鼠标右键,将它改为16进制显示,并将列数改为4列。(一列就代表一个字节),如下:
在这里插入图片描述
通过他们内存中的地址存放信息,我们可以发现,arr数组与i中间间隔了两个整形元素大小,即8个字节,而数组arr一共有10个元素,也就是说,而arr[i],当i ==12时,刚好越界访问到第三个元素!arr[9]与arr[12]中间刚好也隔了两个整形空间。
在这里插入图片描述
即,可以这么来看!
在这里插入图片描述
我们发现,arr[12]中存放的就是i,arr[12]=0也就是相当于i=0,然后再进行循环,无穷无尽。

其实更加标准的答案是:i与arr都是属于局部变量,局部变量是存放在栈区,而栈区的空间使用是从高地址到低地址使用,所以i的地址要高于数组arr的地址,而数组又是随着下标的增长,地址也是由低到高变化,所以当数组arr越界访问适当时,是有可能访问到i的。

(ps:而其实在vs版本里,这里的越界访问空间经测试就是两个整形大小,在gcc环境下是1个整形大小,即在gcc环境下,arr[11]里存放的就是i)

当然,在调试窗口中也可以查看一些别的信息,如下,不过这里用不到,后面学的更加深入后,用到的会比较多一些。
在这里插入图片描述

遇到的常见错误类型介绍

在编程过程中,遇到的错误主要分为:编译型错误、链接型错误、运行时错误。
1、编译型错误
顾名思义,就是在我们编译代码时发生的一些错误,通常都是一些语法错误,对于这种错误我们根据报错提示,便可进行修改,如下所例:
在这里插入图片描述

2、链接型错误
发生在链接期间,在这里我们所犯的错误一般是标识符名不存在/未定义该标识符/未进行声明,或者拼写错误(很常见,假如函数名称很长,很容易写错名字)。如下:
在这里插入图片描述

3、 运行时错误
这一种比较头疼,就是代码可以运行,但是就是要么答案不对,要么程序崩掉(就比如调试实战里的案例),我们在做题时,有时候就会遇到,尤其是显示段错误,或者栈溢出等,这些都属于运行时的错误,遇到这种错误,要么我们就提前对一些边界值、特殊值进行考虑在内,想一想是不是进行越界访问了,还是对空指针进行解引用了,还是存在野指针的使用,还是等等等。。。或者加上断言(后面会讲),然后做题多了后就会长心眼了。

以上便是常见的错误类型。

如何写出好的代码

所谓优秀的代码,无非就以下几个特点:
1.代码运行正常
2. bug很少
3. 效率高
4. 可读性高
5. 可维护性高
6. 注释清晰
7. 文档齐全
常见的技巧:

  1. 使用assert
  2. 尽量使用const
  3. 养成良好的编码风格
  4. 添加必要的注释
  5. 避免编码的陷阱。

接下来讲一下assert与const

assert 与 const(C语言)

这里以strcpy函数为例,我们通过查看官网,发现strcpy是这样的:
在这里插入图片描述
const
这里我们细心的就会发现,第二个参数前面有一个const,那么const是用来干嘛的呢?它有什么作用呢?
这里我们以一个简单的例子来讲解一下:

#include<stdio.h>
int main()
{
	char arr[] = "abcd";
	char* p = &arr;
	printf("%c\n", *p);//a
	*p = 'b';
	printf("%c\n", *p);//b
	p = p + 2;
	printf("%c\n", *p);//c
	return 0;
}


以上这段代码没啥问题吧,运行起来打印出来的是a b c,我们发现p存放的是arr首元素‘a’的地址,可以对*p,或者p的值进行修改,但是假如我们把const放在 *的左边,就会出现以下情况:
在这里插入图片描述
在这里插入图片描述
但假如把const放在 *右边,就会出现以下情况:
在这里插入图片描述
因此,我们有以下结论:

const放在*左边,则指针指向的变量的值不可修改,但是指针变量存放的地址可以修改,const放在 *右边,则指针指向的变量的值可以修改,但是指针变量存放的地址不可修改。
这样做是为了防止一些误操作,增加代码的健壮性,鲁棒性。

我总结了一个便于记忆的方法:可以这么来记:在C语言里,const后面的都不可修改,就比如const int *
p,这里*p不可修改,而int * const p,则p不可修改。

这里回过头来看strcpy就知道为什么加上const了,因为我们要把source的内容拷贝到destinatiion中去,所以这里source的内容肯定不能进行修改,否则拷贝的就不是原来的了。

assert

assert是断言所用,头文件需包含<assert.h>,也就是说,程序只要出现错误,就会立马弹出一个窗口进行警告。 特点:简单粗暴

我们经常用来判断传入的参数是否为空指针,或者用来断言一下别的必要条件,因为有时候,这个编译器不会报错,但是运行起来会直接崩掉,如下所例:
在这里插入图片描述

但是编译器并没有给出警告,只有当我们调试时,才会发生报错:
在这里插入图片描述

那么假如是一段特别长特别长的代码呢?那可就够我们吃一壶的了,不过这里assert的作用就显示出来了,简单粗暴,使用如下:

在这里插入图片描述
先到此为止,总之多调试,多分析,在写代码之前就有清晰的思路以及特殊边界的判断,然后进行编写。


end
生活原本沉闷,但跑起来就会有风!

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

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

相关文章

Springboot集成ItextPdf

目录 一、概述 二、Itext API 1.PDF文档生成 3.常用对象 一、文档对象 二、操作对象 三、内容对象 四、表格对象 四、常用案例 一、水印 三、页眉页脚 四、合并多个PDF 五、表单PDF 六、模板PDF 一、html模板 二、使用工具构建PDF模板 7、HTML转PDF 8、删除页…

同花顺_知识_庄家技法_4拉升技法

写在前面&#xff1a; 文中知识内容来自书籍《同花顺炒股软件从入门到精通》 目录 1. 拉升时机的选择 1&#xff09;大盘走势稳健时 2&#xff09;重大利好出台前后 3&#xff09;进行一次凶狠的打压之后 4&#xff09;大市偏弱时 5&#xff09;图形及技术指标修好之时 …

FPGA开发(4)——AXI_LITE总线协议

一、AXI总线简介 对于axi总线的学习我主要是参考了赛灵思的ug1037文档以及arm的INI0022D手册&#xff0c;对其中的内容做了总结。 AXI是amba总线的一种&#xff0c;包含三种&#xff0c;axi full、axi lite和axi stream。 AXI工作&#xff1a;axi接口包含了五组通道&#xf…

如何快速用一条命令配置好本地yum源(6/7/8版本)

一&#xff0c;挂载ISO安装镜像 挂载方式分两种&#xff1a; 1.上传iso安装镜像到服务器主机的指定目录&#xff0c;比如/setup/os为例 mount -o loop /setup/os/iso镜像包名称 /mnt 2.直接虚拟机或者物理主机挂载iso安装镜像 mount /dev/cdrom /mnt mount/dev/sr0 /mnt 3.挂载…

【计算机网络】网络层:路由器的构成

路由器工作在网络层&#xff0c;用于互连网络&#xff0c;主要工作是转发分组。 把某个输入端口收到的分组&#xff0c;按照分组要去的目的网络&#xff0c;把该分组从路由器的某个合适的输出端口转发给下一跳路由器。 &#xff08;根据目的网络的IP地址转发分组&#xff09;…

[附源码]java毕业设计学生档案管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

使用Umi 遇到的错误

今天想使用ui&#xff0c;出现了很多错误&#xff0c;刚开始安装的时候没有一点事&#xff0c;就是运行的时候报错&#xff0c;好像和umi版本不匹配了&#xff0c;后来又把umi删除了又安装一遍&#xff0c;然后还是运行不了&#xff0c;后来我又把umijs/preset-ui卸了&#xff…

通过制作4个游戏学习Python

前言 学习编程最好玩的方法 你会学到什么 &#xff08;文末送读者福利&#xff09; 您将学习如何有效地使用Python 您将创建一个python游戏组合 你将学会如何管理好大型项目 你将学习面向对象编程 您将学习并实现高级Python特性 你将对Python有一个透彻的理解 类型:电…

Spark并行度和任务调度

文章目录并行度如何设置并行度如何规划我们自己群集环境的并行度&#xff1f;Spark的任务调度并行度 Spark之间的并行就是在同一时间内&#xff0c;有多少个Task在同时运行。并行度也就是并行能力的设置&#xff0c;假设并行度设置为6&#xff0c;就是6个task在并行跑&#xf…

蒙特卡洛原理及实例(附Matlab代码)

文章目录一、理论基础1.1 伯努利大数定理1.2 辛钦大数定理1.3 切比雪夫大数定理1.4 三者区别和联系二、蒙特卡洛法2.1 蒙特卡洛的起源2.2 蒙特卡洛的解题思路2.2 蒙特卡洛法的应用三、几个小栗子3.1 求解定积分3.1.1 解析法3.1.2 蒙特卡洛法3.2 求解六边形面积3.2.1 解析法3.2.…

[附源码]SSM计算机毕业设计基于的高校学生考勤管理系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

支持向量机

目录 支持向量机 0. 由来 1. 核心思想 2. 硬间隔支持向量机 2.1 间隔最大化 2.1.1 函数间隔2.1.2 几何间隔2.1.2 间隔最大化 2.2 转换为拉格朗日对偶问题 2.2.1 拉格朗日对偶问题2.2.2 将问题转换为拉格朗日对偶问题 3. 软间隔支持向量机 4. 泛函基础 4.1 度量&#xff…

Flutter 8 个优秀动画 Packages

Flutter 8 个优秀动画 Packages 前言 动画对于使移动应用程序的用户界面感觉自然流畅至关重要。加上交互式元素和平滑的过渡&#xff0c;它们使应用程序简单易用。 正文 Flutter Animate 组件 Package https://pub.dev/packages/flutter_animate 一个 performant 库&#xff0c…

springboot simple (9) springboot jpa(Hibernate)

返回目录 1 JPA Hibernate Hibernate是一个全自动的ORM框架&#xff08;Object Relational Mapping ,对象关系映射&#xff09;。 Spring Data JPA&#xff1a; 是Spring Data的子模块&#xff0c;JPA默认使用hibernate作为ORM实现。 2 springboot继承Hibernate 第1步&…

【Servlet】7:监听器和过滤器的原理和应用

目录 | 监听器 监听器 基本概述 ServletContextListener监听器 ServletContextAttributeListener监听器 监听器的应用场景 | 过滤器 过滤器 基本概述 过滤器 实现步骤 过滤器 应用场景 本文章属于后端全套笔记的第三部分 &#xff08;更新中&#xff09;【后端入门到入…

leetcode 494.目标和 动态规划背包问题 (c++版本)

题目描述 说白了就是让一部分数减去剩下的一部数使得差值为target&#xff0c;计算有多少中组合的方法 下面来个数学公式推导一下 leftrightsumleft−righttargetleftsum−lefttargetleft(sumtarget)/2leftright sum\\ left-righttarget\\ leftsum-lefttarget\\ left(sumtarge…

用户行为分析-如何用数据驱动增长

用户行为分析-如何用数据驱动增长 2022-11-22 看完书才知道是 GrowingIO 公司出的一本书&#xff0c;干货还是挺多的。 第一章从商业进化的角度认识用户行为数据的重要性&#xff0c;帮助大家了解什么是用户行为数据&#xff0c;以及用户行为数据怎么发挥价值。接着四章详细…

【操作系统】2.2 操作系统的调度

2.2.1 操作系统之处理机调度的概念及层次 2.2.1操作系统之处理机调度的概念及层次_StudyWinter的博客-CSDN博客_操作系统调度的层次 高级调度&#xff08;作业调度&#xff09;&#xff1a;外存-》内存 中级调度&#xff08;内存调度&#xff09;&#xff1a;外存-》内存 低…

用最少的代码模拟gRPC四种消息交换模式

我们知道&#xff0c;建立在HTTP2/3之上的gRPC具有四种基本的通信模式或者消息交换模式&#xff08;MEP&#xff1a; Message Exchange Pattern&#xff09;&#xff0c;即Unary、Server Stream、Client Stream和Bidirectional Stream。本篇文章通过4个简单的实例演示它们在.NE…

HTML+CSS大作业 格林蛋糕(7个页面) 餐饮美食网页设计与实现

&#x1f380; 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…