编程最重要的技术—调试(以C语言代码为例)

简介: 编程最重要的技术—调试(以C语言代码为例)

世界上第一个bug


image.png

  • 第一次被发现的导致计算机错误的飞蛾,也是第一个计算机程序错误。
  • 这只飞蛾就引生出了bug的概念。
  • bug是在应用程序投入生产之前发现的错误。 导致程序工作不佳、生产错误结果或崩溃的编程错误。


1.学会调试很重要

1.1 调试是什么?


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

我们在写完程序后,时常会因为达不到预期效果而苦闷,可以说,代码出现了bug,那么我们如何去寻找程序的错误?

调试是一个简单但又非常重要的技巧,有人说,程序员写代码只要20%的时间,而调试除错占用了80%的时间。这句话非常的形象。

可以说调试就是一个尝试破案的过程,它能够找到导致程序运行效果不佳的错误。

所有发生的事情都一定有迹可循,如果问心无愧,就不需要掩盖也就没有迹象了,如果问心有愧,就必然需要掩盖,那就一定会有迹象,迹象越多就越容易顺藤而上,这就是推理的途径。顺着这条途径顺流而下就是犯罪,逆流而上,就是真相。

所以说一名优秀的程序员是一名出色的侦探。


1.2 调试的基本步骤


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


2.Debug与Release


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



我们看以下这段代码:

#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("hello!\n");
  }
  return 0;
}


在Debug环境下,该程序运行结果为:



b1b390f75c3b49fd843afac3e71a5ae9.png


而在Release环境下运行结果为:

28725e79bbf24f9890e37ebbf3617182.png


可以看到Release的优化使得程序运行没有报错而是正常运行,这体现了Release的特性,当然,Release的优化使得程序不能再进行调试,使得我们不能一步步侦察发现程序的错误所在。

还有一点就是,Release版本文件进行优化后,其文件比Debug版本文件的大小小很多。


3.调试的基本快捷键


设置断点


我们点击代码语句序号前的灰色部分便可以设置断点


55aedc44cb924705a6d5bbddac5e6c29.png


F5

  • 启动调试,经常用来直接跳到下一个断点处。

F9

  • 创建断点和取消断点;
  • 断点的重要作用,可以在程序的任意位置设置断点;
  • 这样就可以使得程序在想要的位置随意停止执行,继而一步步执行下去。

F10

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

F11

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


ctrl + F5


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

以上这些基本的快捷键必须要掌握。


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


4.1 查看临时变量的值

  • 在调试开始之后,用于观察变量的值。
  • 调试开始后点击调试,在窗口处选择监视(1,2,3,4,随便点击一个),当然也可以选择自动窗口(程序自动显示出变化的量)。

在监视窗口中,我们可以输入想观测量的信息从而进行观测。


a7857b9fe7be42f099fdb0f86166010e.png

4.2 查看内存信息

  • 在调试开始之后,用于观察内存信息。
  • 用于观察变量在内存中的地址。
  • 用于观察变量此时的大小。

4b29f69b866e49569fa4c75c97e27986.png


4.3 查看调用堆栈

  • 通过调用堆栈,可以清晰的反应函数的调用关系以及当前调用所处的位置。
  • 可以观察函数的进栈和出栈。

fe01c8e2b0a741a2a0f1a406d910d522.png


4.4 查看汇编信息

  • 在调试开始之后,有两种方式转到汇编:
    1.第一种方式:右击鼠标,选择【转到反汇编】;
    2.在调试窗口选项里可以进入。
  • 查看汇编信息可以让我们更加清楚在VS环境下程序是如何运行的(代码运行的顺序)。


0421640042d74e7ea924919ca1cea8ef.png



37f79caf3db04e29941d8c8d07acac5d.png


调试的时候查看程序当前的信息可以是我们更加的贴近代码运行的过程,从而使我们的思维更加的细化。


5.如何写出好的代码


  1. 代码运行正常
  2. bug很少
  3. 效率高
  4. 可读性高
  5. 可维护性高
  6. 注释清晰
  7. 文档齐全
  • 常见的coding技巧:
  1. 使用assert
  2. 尽量使用const
  3. 养成良好的编码风格
  4. 添加必要的注释
  5. 避免编码的陷阱。
  • 示范:
    模拟实现库函数:strcpy


#include <stdio.h>
#include <assert.h>
char* my_strcpy(char* dest,const char* sor)
{
  assert(dest && sor);
  char* n = dest;
  while (*dest++ = *sor++)
  {
    ;
  }
  return n;
}
int main()
{
  char arr1[1000] = "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$";
  char arr2[1000];
  printf("开始的arr1:");
  printf("%s\n", arr1);
  printf("请输入要复制过去的内容:\n");
  gets(arr2);
  printf("复制后的arr1:");
  printf("%s\n", my_strcpy(arr1, arr2));
  return 0;
}


  • 注意:
    1.要分析参数的设计(命名,类型),返回值类型的设计;
    2.野指针和空指针的危害。


6.编程常见的错误


6.1 编译型错误

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


6.2 链接型错误

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

6.3 运行时错误

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

温馨提示:

做一个有心人,积累排错经验。

讲解重点:

介绍每种错误怎么产生,出现之后如何解决。

总结

  • 一定要熟练掌握调试技巧。
  • 初学者可能80%的时间在写代码,20%的时间在调试。但是一个程序员可能20%的时间在写程序,但是80%的时间在调试。
  • 这里所讲的都是一些简单的调试。
  • 以后可能会出现很复杂调试场景:多线程程序的调试等。
  • 多多使用快捷键,提升效率。


最重要的一点:

当我们在看别人的代码看不懂时,一定要将其代码调试去仔细看看其中程序运行的量的变化过程,然后再思考总结,领悟别人的解题思维,这样才能将知识灌输到自己的大脑中,而不是看到不懂的或者是不会的就去问,当然好问也是一种技巧,但是一定是思考过的去问才能达到获知的效果!

相关文章
|
存储 编译器 C语言
【C语言】数据类型全解析:编程效率提升的秘诀
在C语言中,合理选择和使用数据类型是编程的关键。通过深入理解基本数据类型和派生数据类型,掌握类型限定符和扩展技巧,可以编写出高效、稳定、可维护的代码。无论是在普通应用还是嵌入式系统中,数据类型的合理使用都能显著提升程序的性能和可靠性。
786 8
|
存储 算法 程序员
C 语言递归算法:以简洁代码驾驭复杂逻辑
C语言递归算法简介:通过简洁的代码实现复杂的逻辑处理,递归函数自我调用解决分层问题,高效而优雅。适用于树形结构遍历、数学计算等领域。
|
C语言 开发者
C语言中的模块化编程思想,介绍了模块化编程的概念、实现方式及其优势,强调了合理划分模块、明确接口、保持独立性和内聚性的实践技巧
本文深入探讨了C语言中的模块化编程思想,介绍了模块化编程的概念、实现方式及其优势,强调了合理划分模块、明确接口、保持独立性和内聚性的实践技巧,并通过案例分析展示了其应用,展望了未来的发展趋势,旨在帮助读者提升程序质量和开发效率。
930 5
|
C语言
C语言编程中,错误处理至关重要,能提升程序的健壮性和可靠性
C语言编程中,错误处理至关重要,能提升程序的健壮性和可靠性。本文探讨了C语言中的错误类型(如语法错误、运行时错误)、基本处理方法(如返回值、全局变量、自定义异常处理)、常见策略(如检查返回值、设置标志位、记录错误信息)及错误处理函数(如perror、strerror)。强调了不忽略错误、保持处理一致性及避免过度处理的重要性,并通过文件操作和网络编程实例展示了错误处理的应用。
576 4
|
存储 算法 C语言
用C语言开发游戏的实践过程,包括选择游戏类型、设计游戏框架、实现图形界面、游戏逻辑、调整游戏难度、添加音效音乐、性能优化、测试调试等内容
本文探讨了用C语言开发游戏的实践过程,包括选择游戏类型、设计游戏框架、实现图形界面、游戏逻辑、调整游戏难度、添加音效音乐、性能优化、测试调试等内容,旨在为开发者提供全面的指导和灵感。
876 2
|
NoSQL 编译器 C语言
C语言调试是开发中的重要技能,涵盖基本技巧如打印输出、断点调试和单步执行,以及使用GCC、GDB、Visual Studio和Eclipse CDT等工具。
C语言调试是开发中的重要技能,涵盖基本技巧如打印输出、断点调试和单步执行,以及使用GCC、GDB、Visual Studio和Eclipse CDT等工具。高级技巧包括内存检查、性能分析和符号调试。通过实践案例学习如何有效定位和解决问题,同时注意保持耐心、合理利用工具、记录过程并避免过度调试,以提高编程能力和开发效率。
686 1
|
人工智能 安全 算法
基于C语言的嵌入式系统开发,涵盖嵌入式系统概述、C语言的优势、开发流程、关键技术、应用实例及面临的挑战与未来趋势。
本文深入探讨了基于C语言的嵌入式系统开发,涵盖嵌入式系统概述、C语言的优势、开发流程、关键技术、应用实例及面临的挑战与未来趋势。C语言因其高效、可移植、灵活及成熟度高等特点,在嵌入式系统开发中占据重要地位。文章还介绍了从系统需求分析到部署维护的完整开发流程,以及中断处理、内存管理等关键技术,并展望了嵌入式系统在物联网和人工智能领域的未来发展。
897 1
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
707 1