【C语言】实用调试技巧

前言

一、什么是bug?

二、调试

2.1 调试是什么?

2.2 调试的基本步骤

2.3 Debug和Relese的介绍

2.4 调试常用快捷键

 2.5 调试的时候查看程序当前信息

2.5.1 查看临时变量的值

2.5.2 查看内存信息

2.5.3 查看调用堆栈

2.5.4 查看汇编信息

2.6 如何写出易于调试的代码

三、编译常见的错误

3.1 编译型错误

3.2 连接型错误

3.3 运行时错误

总结


前言

在编写代码时,是否有因为出现Bug而烦恼呢?记得网上有个段子讲到:一个程序员百分之二十的时间在写Bug,百分之八十的时间在修改Bug。由此可见,掌握一些实用的调试技巧,能提高我们查找Bug的效率,本文以Visual Studio 2019为例,详细讲解如何利用该编译器工具进行代码的调试。


一、什么是bug?

软件缺陷(Defect),常常又被叫做Bug。 [1]所谓软件缺陷,即为计算机软件或程序中存在的某种破坏正常运行能力的问题、错误,或者隐藏的功能缺陷。缺陷的存在会导致软件产品在某种程度上不能满足用户的需要。IEEE729-1983对缺陷有一个标准的定义:从产品内部看,缺陷是软件产品开发或维护过程中存在的错误、毛病等各种问题;从产品外部看,缺陷是系统所需要实现的某种功能的失效或违背。具体可参考百度百科:软件缺陷_百度百科 (baidu.com)

二、调试

2.1 调试是什么?

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

2.2 调试的基本步骤

  • 发现程序错误的存在

这步通常是由以下三类人发现的:

  1. 程序员:在编写代码时,发现程序的错误
  2. 测试:程序编写完毕后,由测试人员发现
  3. 用户:程序上线后,被用户发现
  • 以隔离、消除等方式对错误进行定位
  • 确定错误产生的原因
  • 提出纠正错误的解决方法
  • 对程序错误予以改正,重新测试

2.3 Debug和Relese的介绍

在Visual Studio 编译器中,有两个版本,分别时Debug版本和Relese版本。 

 Debug:调试版本(一般用于程序员调试程序)

 Relese:发布版本(一般用于测试人员测试)

 当然,不同的版本是由区别的,区别体现在两方面:

运行后生产的程序大小不同: 

Debug版本后的程序大小

Relese版本后的程序大小
  •  Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。
  • Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优 的,以便用户很好地使用。

进入调试后,效果不同: 

  •  调试时,Relese版本不能进行调试,直接给出结果
  • Debug可以一步一步观察,可以调试

2.4 调试常用快捷键

F5 - 开始调试

  • 开始调试,经常用来直接跳到下一个断点处。通常与F9配合实用

F9 - 创建断点和取消断点

  • 什么是断点?对某条代码设置断点,意思就是该代码前的代码不需要调试,直接从该断点处开始调试,调试时应使用F5开始调试快捷键,会直接到断点处。
  • 设置断点后,如果还想设置条件,如在循环中,想让i=某次,可以对鼠标右键点击条件,设置条件

F10 -  逐过程

  • 通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句。
  • 不能进入函数内部,查看函数内部细节

F11 - 逐语句

  •  就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部(这是最 长用的)。

Ctrl + F5

  •  开始执行不调试,如果你想让程序直接运行起来而不调试就可以直接使用。

注意:

  • 有些电脑要配合fn一起使用,根据具体情况。 
  • F10和F11功能大致相同,不同点在于遇到函数时。

2.5 调试的时候查看程序当前信息

2.5.1 查看临时变量的值

这里介绍一个功能,用于查看调试过程中变量的值,可以仔细观察变量值的变化。

输入要监视的变量

2.5.2 查看内存信息

如果想查看一个变量对应的内存信息,也是有对应方法的。 

如查看str的地址,则在输入框输入即可

2.5.3 查看调用堆栈

什么是栈?其实是一种数据结构,特点是先进后出。调用堆栈功能一般用于有多个函数时,在C语言中,函数和局部变量是存放在栈区的,使用调用堆栈就能观察每个函数和局部变量的创建过程。 

2.5.4 查看汇编信息

如何查看C语言翻译出来的汇编代码。

 相同的,窗口功能下还有很多工具,老铁们可以自行测试。

2.6 如何写出易于调试的代码

优秀的代码:

  •  代码运行正常
  • bug很少
  • 效率高
  • 可读性高
  • 可维护性高
  • 注释清晰
  • 文档齐全

常用的coding技巧: 

  • 使用assert
  • 尽量使用const
  • 养成良好的编码风格
  • 添加必要的注释
  • 避免编码的陷阱。

2.7 特殊代码

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

您认为结果是多少呢?大部分人看到该代码,第一反应是数组越界了,确实,该程序数组越界了,但我想告诉你,这个代码运行后是死循环,是不是很惊讶!

 为什么呢?这是因为编译器分配空间的问题,我们知道局部变量或者函数是存放在栈区的,栈区是由低地址向高地址存储的,并且,在Visual Studio 2019中,分配给每个变量空间时,是间隔两个内存空间进行分配,因此这里的变量i和数组arr,其实是隔两个内存空间,因此当循环i++时,arr数组大小为10,当访问到 i == 12时,并且赋值为0,其实访问到了i的内存空间,将i的值改了,因此造成死循环。

当然,栈区中,每个变量之间间隔几个空间是由编译器决定的,可以得出:

Visual Studio中:2个

Linux下gcc中:1个

三、编译常见的错误

3.1 编译型错误

 直接看错误提示信息(双击),解决问题。或者凭借经验就可以搞定。相对来说简单。

3.2 连接型错误

 看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是标识符名不 存在或者拼写错误。

3.3 运行时错误

 借助调试,逐步定位问题。最难搞。


总结

写代码出现Bug是不可避免的,希望通过本篇文章,能帮助到大家更加轻松的解决Bug。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值