本文介绍了什么是bug,什么是调试,调试重要性,如何调试解决bug,各种常用的调试快捷键,如何写出好代码以及const关键字,assert断言库函数介绍,写代码各种遇见的错误, 调试训练

实用调试技巧

  • 一.什么是bug
  • 二.调试是什么?有多重要?
    • 1.调试是什么?
    • 2.调试的基本步骤.
    • 3.debug和Release的介绍
  • 三.Windows环境调试介绍.
    • 1.调试环境的准备
    • 2.学会使用快捷键
    • 3.调试的时候查看程序当前信息
      • ①查看临时变量的值
      • ②查看内存信息
      • ③查看调用堆栈
      • ④查看汇编信息
      • ⑤查看寄存器信息
  • 四.多多动手.尝试调试.才会有进步!
  • 五.如何写出易于调试的代码.
    • 1.优秀的代码
    • 2.常见的coding技巧
    • 3.模拟实现库函数strcpy(字符串拷贝函数)
    • 4.assert断言函数
    • 5.const 关键字介绍
  • 六.编译常见的错误
    • 1.编译型错误
    • 2.链接型错误
    • 3.运行时出错
  • 七.学以致用,调试训练
    • 1.分析代码,找出问题
    • 2.对代码出现的问题图解
  • 八.总结

一.什么是bug

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

上图,这只虫子是世界上出现的第一个计算机bug,
事件发生在1947年9月9日,葛丽丝·霍普(Grace Hopper)发现了第一个电脑上的bug。当在Mark II计算机上工作时,整个团队都搞不清楚为什么电脑不能正常运作了。经过大家的深度挖掘,发现原来是一只飞蛾意外飞入了一台电脑内部而引起的故障(如图所示)。这个团队把错误解除了,并在日记本中记录下了这一事件。也因此,人们逐渐开始用“Bug”(原意为“虫子”)来称呼计算机中的隐错。 —— 维基百科

在我们用编程语言写源程序时,在写程序过程中,bug也就难免会出现了,有什么办法自己写的程序一定没有bug呢?
简单,不写程序就没有bug了~~(这是不现实的)…
在程序员的世界里,每天都在和bug打交道,可以说不是在找bug,就是在找bug的路上…
而今天要讲的就是如何通过编译器精确找bug 的技巧,也成调试技巧.

二.调试是什么?有多重要?

.每个bug出现都是有迹可循的,就像我们生活中 所有发生的事情都一定有迹可循,如果问心无愧,就不需要掩盖也就没迹象了,
如果问心有愧,就必然需要掩盖,那就一定会有迹象,迹象越多就越容易顺藤而上,这就是推理的途径。
顺着这条途径顺流而下就是犯罪,逆流而上,就是真相。
而一名优秀的程序员是一名出色的侦探。每一次调试都是尝试破案的过程

1.调试是什么?

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

2.调试的基本步骤.

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

3.debug和Release的介绍

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

这串代码在32位平台下进行编译
如果是 debug 模式去编译,程序的结果是死循环。

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

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

三.Windows环境调试介绍.

主要介绍Windous环境在vs编译器下的调试.

1.调试环境的准备

在环境中选择Debug选项,才能使代码正常调试.

2.学会使用快捷键

在菜单栏处调试选项下对应的调试方式,但每次调试都去找对应的方式有点浪费时间.最好记忆一下对应的快捷键.

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

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

一些常用的调试查看程序当前信息的操作 得在调试过程中才能查看

①查看临时变量的值

在调试开始后,查看变量的值
调试->窗口->监视->任意一个监视窗口->在弹出的监视窗口里 查看想查看的变量值

②查看内存信息

在调试开始之后,用于观察内存信息。
调试->窗口->内存->任意选一个内存窗口->在地址栏输入对应变量的地址查看该地址指向的位置上的变量内存信息

③查看调用堆栈

通过调用堆栈,可以清晰的反应函数的调用关系以及当前调用所处的位置
调试->窗口->调用堆栈 (注意调试的时候要按f11逐语句进入函数,可以查看当前在哪个函数堆栈)

④查看汇编信息

查看代码对应的的汇编代码
在调试开始之后,有两种方式转到汇编:

(1)第一种方式:右击鼠标,选择【转到反汇编】:

(2)第二种方式:调试->窗口->反汇编.

⑤查看寄存器信息

可以查看当前运行环境的寄存器的使用信息
调试->窗口->寄存器

四.多多动手.尝试调试.才会有进步!

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

五.如何写出易于调试的代码.

多看,多学,多动手写代码,不积跬步无以至千里,不积小流无以成江河!!!

1.优秀的代码

  1. 代码运行正常
  2. bug很少
  3. 效率高
  4. 可读性高
  5. 可维护性高
  6. 注释清
  7. 文档齐全

2.常见的coding技巧

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

3.模拟实现库函数strcpy(字符串拷贝函数)

/***
*char *strcpy(dst, src) - copy one string over another
*
*Purpose:
* Copies the string src into the spot specified by
* dest; assumes enough room.
*
*Entry:
* char * dst - string over which "src" is to be copied
* const char * src - string to be copied over "dst"
*
*Exit:
* The address of "dst"
*
*Exceptions:
*******************************************************************************/
#include<stdio.h>
#include<assert.h>
char* strcpy(char* dst, const char* src)
{char* cp = dst;assert(dst && src);while (*cp++ = *src++); /* Copy src over dst */return(dst);
}
int main()
{char arr[] = "hello";char str[] = "Hello";printf("%s", strcpy(arr, str));return 0;
}

上面是库函数strcpy字符串拷贝函数的模拟实现 涉及了 const 关键字 和assert断言函数

4.assert断言函数

assert是一个库函数,使用它对应要包含头文件#include<assert.h>
它的作用是:当后面条件为真时,程序可以继续运行,当为假时,程序在此处停住不能继续执行并弹出断言提示信息便于确定是那一个地方断言失败停止了.

当传过去的参数是一个NULL时,因为要拷贝字符串,这个字符串肯定不能是NULL,此时设置相应的断言函数,以防传过去是NULL导致程序崩溃错误.此时断言会直接报错断言失败.此时可以精确找到程序断言失败的位置将其修改,而需要一步步调试找错

5.const 关键字介绍

const关键字修饰变量表示该变量具有常属性,是一个常变量,变量里的值在创建并初始化后就不能再更改了.具有只读属性.有了const修饰使得这个变量更安全,里面的数不能被任意修改.

先看一段代码,看看这串代码对应的语句是否都是能正常运行的

 #include <stdio.h>
//代码1
void test1()
{int n = 10;
int m = 20;
int *p = &n;
*p = 20;//ok?
p = &m; //ok?
}
void test2()
{//代码2
int n = 10;
int m = 20;
const int* p = &n;
*p = 20;//ok?
p = &m; //ok?
}
void test3()
{int n = 10;
int m = 20;
int *const p = &n;
*p = 20; //ok?
p = &m; //ok?
}
int main()
{//测试无cosnt的
test1();
//测试const放在*的左边
test2();
//测试const放在*的右边
test3();
return 0;
}

程序运行有两处地方出错,可以看出const在修饰指针变量又有所不同.
const修饰指针变量的时候:

  1. const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改 变。但是指针变量本身的内容可变。
  2. const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指 针指向的内容,可以通过指针改变。

六.编译常见的错误

1.编译型错误

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

2.链接型错误

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

3.运行时出错

程序一般可以运行,但是运行可能导致程序崩溃(栈溢出),或者运行最后得到的结果不是自己想要的.
需要借助调试,逐步定位问题。最难搞

七.学以致用,调试训练

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

这串代码在上面说到在32位平台,debug选项下执行程序会造成死循环
而正常肉眼看的情况,这串代码会直接报错越界访问,但此时确可以运行.
这串代码单凭肉眼是不可能看出执行过程的.此时我们要通过调试一步步找原因

通过调试发现,i循环前十次,将数组里十个元素都赋值为了0,但i此时仍然小于12会继续循环,此时将arr[10],arr[11],arr[12]都赋值为了0,但这三个变量都是属于数组外的内容,本来应该是越界访问出错,
但仔细观看arr[12]和i的地址发现,它们竟然完全相同,这说明i和arr[12]用的同一个地址,而地址是独一无二的,说明它们是同一块空间,将此时arr[12]赋值为0,同时i也变为了0,而经过i++后i变为1 此时仍然小于12,继续将arr[1]赋值为0,重复前面的操作最后到arr[12]仍然将自身改为了0,而因为这个循环一直没有中止,此时程序无法停止报错,从而造成了死循环打印hehe!!
而为什么会发生这种情况呢?

2.对代码出现的问题图解

注意:在不同编译器不同环境下,创建的变量间的间隔是不同的,

八.总结

很多情况下,我们无法通过肉眼看出程序中存在的bug,调试就显得尤为重要
它像程序员手里的一把利剑,学会使用它,斩尽一切干扰程序正常运行的臭虫
使用它的秘籍就是:做一个有心人,积累排错经验。

写文不易,给一个一键三连,支持下叭~~~

还在肉眼找bug??赶紧进来!!!程序员一定要学的调试技巧.相关推荐

  1. 80%的程序员都不了解的调试技巧

    转载自   80%的程序员都不了解的调试技巧 程序员的工作内容,除了大部分时间写代码之外,因为有不少的时间是用在调试代码上.甚至说不是在调试代码,就是即将调试代码. :) 今天我们来谈谈调试代码的一些 ...

  2. 实习生两分钟解决程序bug,老程序员笑笑不说话,还是太年轻?

    又到一年高校毕业季,为了能实现更好就业,大学生们也是使尽了浑身解数. 从某种程度上来说,985.211高校毕业的大学生找工作要比普本院校毕业的学生要容易很多,现在也有不少985高校的毕业生已经进入了实 ...

  3. 找工作就像找对象,愿程序员不再孤单。。。

    找工作就像找对象,愿程序员不再孤单... 在这个牛郎与织女相会的节日,我想告诉你一个爱情的真理:搞对象,总得有一个人先耍流氓.别在骚浪贱的年纪选择端庄,"俺稀罕你,你稀罕俺不",一 ...

  4. 50岁还在写代码,大龄前端程序员到底有多吃香

    大家好呀!我是小千学姐 程序员圈流传着这样一个段子:35岁之后,干前端去送外卖:搞后端去开滴滴:运维就去买保险-- 招人不要35岁的,裁人清理35岁的,不知道什么时候开始,35岁成了职业生涯的拐点,中 ...

  5. 36岁转行学java_年纪大了还想转行当程序员,现在学java还来得及吗

    现在流行这样一种论调:说程序员就是吃青春饭的,年纪大了就不要再转行当java程序员了.可是仍然还有许多人前赴后继的想来学java当程序员,但是心里面有隐约担心:年纪也不小的,快30了还能学java吗? ...

  6. 学了go语言再学java容易吗_年纪大了还想转行当程序员,现在学java还来得及吗-Go语言中文社区...

    现在流行这样一种论调:说程序员就是吃青春饭的,年纪大了就不要再转行当java程序员了.可是仍然还有许多人前赴后继的想来学java当程序员,但是心里面有隐约担心:年纪也不小的,快30了还能学java吗? ...

  7. 专业程序员必知必会的技巧:驯服复杂代码

    你从入职第一天起就要应对复杂代码. 若是还未遇到过无法理解的程序,那说明你编程的年头还不够长.在行业里,要不了多久你就会碰到让人发懵的混乱代码:巨兽.面条工厂.来自地狱的遗留系统.我曾接手过一个程序, ...

  8. 不写情书,程序员为什么还要学写作?

    跟小伙伴们一样,老兵哥也年轻过,在花季雨季也写过情书,不过经常石沉大海.当时特别希望自己的文笔好一些,像小说影视作品中的男猪脚那样一封书信就可以俘获菇凉的芳心.现今娃娃都能打酱油了,原来情书的效用早就 ...

  9. 技术好却进不了大公司?程序员面试的几个小技巧请收好

    技术好却进不了大公司?程序员面试的几个小技巧请收好 有很多程序员,专业技术挺好,项目经验充足,简历上金碧辉煌,也面试了很多国内大厂,但就是没有一家能成功的.处境可以说是相当尴尬. 大家都知道程序员本身 ...

最新文章

  1. Cell子刊:粘上你-细菌生长素介导的植物根部细菌定殖
  2. 复制并重命名文件_视频文件太大怎么办?快速压制视频只需一招,好多大咖都在用...
  3. Oracle超出最大连接数问题及解决
  4. 【HTML/CSS】HTML5和CSS3的新特性
  5. 如何插入一条记录获取插入后的自动增长ID列的方法.
  6. mysql复制(高可用架构方案的基础)
  7. 两种通用型测试用例模板(excel/xmind)
  8. 高斯求积分公式matlab,matlab高斯积分公式
  9. 一颗电阻失效引发的设计思考
  10. 百词斩不复习_不背单词好用还是百词斩好用?
  11. 自棱镜事件,隐私保护搜索引擎DuckDuckGo流量增长600%
  12. Ubuntu20.04的一些功能设置记录(持续更新)
  13. 对数据驱动运营的理解
  14. 移动OA,为企业提供更高效的办公模式
  15. ISCSI服务器搭建与配置
  16. I lost my spoon
  17. C语言版-商品销售管理系统
  18. max(label_counts, key=label_counts.get)的理解
  19. 实验室仪器设备管理方案
  20. HIT 软件构造 bug debug

热门文章

  1. 基于ssh羽毛球馆管理系统
  2. H264的基本原理(二)------ H264编码原理
  3. 【DBeaver】介绍DBeaver这一款强大的数据库连接工具
  4. 机器学习-预测-线性系统的预测(最小二乘法、正规方程式实现)
  5. 手机联网总是显示正在连接服务器,手机老显示网络连接超时是什么意思
  6. k-dimensional tree
  7. 文献阅读:《2016年中国癌症发生及死亡统计》
  8. 按照珠算口诀进行计算的数字计算机
  9. 黑客必读:蜜罐技术的前世今生
  10. Docker下载加速:Docker镜像下载加速、pip 下载加速、apt 下载加速