适合初学者的超详细实用调试技巧(上)一

简介: 适合初学者的超详细实用调试技巧

我们日常写代码的时候,常常会遇到bug的情况,这个时候像我这样的初学者就会像无头苍蝇一样这里改改那里删删,为了根除这种情况,我最近系统学习了调试的技巧,我想要十分详细地讲解,所以大概不会一篇文章写完。


大概分为以下几个部分:


  1. bug的来历以及它到底是什么?
  2. 调试是什么?为什么程序员要学习调试?
  3. debug和release的介绍。
  4. windows环境调试介绍。
  5. 一些调试的实例。
  6. 如何写出好(易于调试)的代码。 编程常见的错误。


1.bug的来历以及它到底是什么?


下图这只臭名昭著的飞蛾意外走入一电脑而引致故障。


64852d0ca693be726418fd4641978b58_b4b8a1407bf2026d10008122dc816a59.jpeg


一天,计算机发生故障,工作人员赫柏经过排查,在计算机的继电器触电里,找到了一只被夹扁的小飞蛾,这只小虫子卡住了机器的运行,赫柏顺手将飞蛾夹在工作笔记里,并诙谐的把程序故障称为“bug”。


这就是我们今天最爱说的“bug”的由来。它的意思,和原身一致,真就是“一只臭虫”。


下图就是赫柏:


f93bc4a2b0fa54d9562c7bf972c1088e_format,png.png


1906年,赫柏出生在美国纽约。童年的赫柏,展现出了不同于一般小女孩的爱好:爬树、游泳、划船、捉迷藏,热衷于一切动来动去上蹿下跳的活动。从上学起,赫柏在数学、物理方面都异常出色。世俗眼中最美好青春的年华,赫柏都花在了学问的深造上。利用获得的奖学金,赫柏再次考进耶鲁大学深造,2年后取得数学硕士学位,继而又攻读博士学位,成为了耶鲁大学历史上第一位女数学博士。也成为了程序员们的“bug女神”。


2.调试是什么?为什么程序员要学习调试?


对于一个真正的推理家而言,如果有人指给他一个事实的其中一个方面,他不仅能推断出这个事实的各个方面,而且能够推断出由此将会产生的一切后果。正如居维叶经过仔细思考就能根据一块骨头准确地描绘出一头完整的动物一样。一个观察家,既已透彻了解一系列事件中的一个环节,就应能准确地说出前前后后的所有其他的环节。我们还没到只要掌握理性就能获得结论的地步。问题只有通过研究才能获得解决,想仅仅依靠直觉解决问题,最后一定会失败的。


这是福尔摩斯的一句话,程序员的调试也要像个侦探一样。


一名优秀的程序员是一名出色的侦探。每一次的调试都是破案的过程。


我们是如何写代码的?


不进行调试,而是不断地东改西改,耗费大量时间,最后自己都不知道怎么是怎么成功运行的。


这样的程序员是不合格的,这就要求我们学会调试。


2.1 调试是什么?


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


2.2 调试的基本步骤


发现程序错误的存在

以隔离、消除等方式对错误进行定位

确定错误产生的原因

提出纠正错误的解决办法

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


2.3 Debug和Release的介绍


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


#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
  int a = 10, b = 20;
  printf("%d", a + b);
  return 0;
}

看如上代码在debug和release环境时不同的情况。


上述代码在Debug环境的结果展示:


fa6a0e98e9fcc6051bf7713f79da3c37_9aefc62837f14b38bc691a94d17b0fa1.png


上述代码在Release环境的结果展示:


36c5ab5b22bf729d3cc29a70420ea838_ccf9ef6b2130404689e883c05e73de56.png


可以明显看到,release版本代码的大小明显小于debug版本,这是优化导致的。


代码的大小在反汇编显示出来。也能如果代码是错误的 ,在反汇编也会显出错误。


#include<stdio.h>
int main()
{
  char p[100] = "I.m handsome";//改正之后
  printf("%s", p);
  return  0;
}

Debug和Release反汇编展示对比:


47bec2b841dd54e108e72b3c3efdb173_7ccca26d3ce9403a9922d8765740198f.png


b37315a6827fbeaff1e4b6feb5ce5703_2887c7c1b2a94309950e3171478a46b7.png


可以看到debug版本明显复杂的多,release版本由于做了优化则十分精简。




所以我们说调试就是在Debug版本的环境中,找代码中潜伏的问题的一个过程。


那编译器进行了哪些优化呢?请看如下代码。


#include <stdio.h>
int main()
{
    int i = 0;
    int arr[10] = {0};
    for(i=0; i<=12; i++)
   {
        arr[i] = 0;
        printf("I.m handsome.\n");
   }
    return 0;
}

如果是 debug 模式去编译,程序的结果是死循环。


如果是 release 模式去编译,程序没有死循环。


那他们之间有什么区别呢? 就是因为优化导致的


在vs编译器的debug 环境下,变量和数组之间开辟两个字节。在 i==12 的时候,i的栈区和arr[10]重合,arr[10]=0的指令让i也变成了0。


95b28b932903e3d2b4709c4c400b29b4_48a06ccbb842a3b3de185c5b8026bfc3.png


变量在内存中开辟的顺序发生了变化,影响到了程序执行的结果。



相关文章
|
6月前
|
缓存 前端开发 JavaScript
【第22期】 一文读懂前端调试利器whistle
【第22期】 一文读懂前端调试利器whistle
124 0
|
2月前
|
Go iOS开发 MacOS
学习笔记-代码调试工具
dlv(delve的简称)是一个强大的Go源代码级调试工具,通过控制程序执行、计算变量及提供线程/协程状态等信息,为调试Go程序提供了简单且功能丰富的界面。在macOS上,可以通过`brew install dlv`命令安装。下面是一个使用dlv调试Go代码的例子,展示了如何设置断点并逐步执行代码,检查变量值和函数内部细节。常用命令包括:`b`(打断点)、`p`(打印变量)、`n`(下一步)、`c`(继续执行)、`args`(打印参数)、`locals`(打印局部变量)、`l`(列出代码)、`bp`(显示断点)和`q`(退出)。
43 2
|
5月前
|
存储 编译器 C语言
C语言学习记录——调试技巧(VS2019环境下)
C语言学习记录——调试技巧(VS2019环境下)
53 2
|
5月前
|
Shell 数据安全/隐私保护 开发者
详细解读ApolloGPS调试笔记
详细解读ApolloGPS调试笔记
24 0
|
6月前
|
存储 程序员 编译器
程序员必备技能——调试技巧(二)
程序员必备技能——调试技巧(二)
50 0
|
6月前
|
程序员 Windows
程序员必备技能——调试技巧
程序员必备技能——调试技巧
90 0
|
6月前
|
机器学习/深度学习 图计算 异构计算
|
IDE 程序员 编译器
必备技能:程序的调试
本节内容相对来说比较笼统一些,但是也是非常非常非常重要的一节内容,毕竟一个程序猿不能只会写bug,你写一堆bug谁来修呢?所以通过调试去排查错误的能力是一个优秀的程序猿必须要掌握的技能,而且不能仅仅是掌握,更要精通,本节内容总体来说只有两大部分,一是学习调试的方法,会有举例实际去感受一下,二是在写代码就要考虑的一些问题,养成良好的写代码习惯,防患于未然。
必备技能:程序的调试
|
程序员 Windows
适合初学者的超详细实用调试技巧(上)二
适合初学者的超详细实用调试技巧
72 0
|
安全 程序员
适合初学者的超详细实用调试技巧(下)二
适合初学者的超详细实用调试技巧
48 0