C语言(调教你的代码)

2019-08-08 11:23:28 浏览数 (1)

写一篇程序就像谈一场恋爱,一篇一笔写就丝丝入扣毫无破绽扩展性好且兼容性强最终达到完美无瑕的程序,就像一场青梅竹马烈火烹油如胶似漆最后白头偕老的故事,它们基本都属于童话,童话里都是骗人的!那就有个疑问了,开发者是怎么调试代码的呢?

问题的伊始,我们第一个需要搞清楚的是你的程序的规模,一般而言,在公司中开发的程序软件,要比初学者刚开始做练习用的代码的规模要大得多。公司做一个项目,就像盖一栋有一定规模的大楼,不可能没有任何设计布局直接就上钢筋和水泥,那些必不可少的前期准备包括:

1)需求文档 2)可行性文档 3)接口文档 4)用户使用手册 ……

以上的文档和手册并不一定都需要,实际上,编写规范内容详实的文档是项目开发的重要部分,有些刚入手的小白不喜欢文档类工作,也难怪,小工匠刚开始的时候大多无法理解大厦设计的蓝图,一心想要马上着手垒砖砌墙。但是随着年岁经验的增长,一句至理名言会慢慢浮现在你脑海:磨刀不误砍材工。

我们以前所在公司团队就被客户虐过。。。那厮不懂程序逻辑,但非要提出很多不可思议的要求,且无法形成需求文档,于是我们写了改改了写,每次都不合意,在鸡同鸭讲的语言环境和步步紧逼的验收日期中,多少锐意青年愁白了头。再说一次,规范详实的各类开发文档,是程序开发中必不可少的重要组成部分

好了不说文档了,就说程序代码本身吧。由浅入深由易到难,程序毛病的探查方法的次序应该如下

  1. 语法错误,编译器gcc自己就能搞定
  2. 简单逻辑错误,使用打印语句(比如printf或者printk)将程序中关键信息罗列出来,然后用火眼精金来识别
  3. 段错误,此类错误是最常见的错误,肉眼看不出来的话,就借助所谓core文件和gdb来定位出错的地方。注意这个办法要拼人品的,不是每次都能成功定位。
  4. 复杂逻辑错误,在以上办法都无法查验错误之所在时,只能硬着头皮使用调试器gdb来单步调试。注意使用这个办法的时候状态是硬着头皮,足以说明这是不得已而为之的无奈之举。

下面逐个解释一下。

第一,语法错误。这个没什么好讲的,gcc编译的时候就会报错了,根据错误信息一个个更正就行了。

第二,简单错误,打印相关信息。比如以下代码:

此时第6-8行都属于调试类的代码,跟程序本身的实际功能并无关联。这类代码可以通过是否定义宏DEBUG来方便地进行增删。比如在调试阶段,我们这么编译,使能调试语句:

gcc a.c -o a -DDEBUG

而当程序正式发布阶段,我们这么编译,删除那几行调试语句:

gcc a.c -o a

第三,段错误。此类错误的英文是Segmentation fault,即所谓的非法内存访问。产生这类错误的情况有很多,常见的是:

  1. 对未初始化的指针进行取目标
  2. 对内存引用越界
  3. 重复释放已经释放了的内存

如果程序代码比较复杂,无法马上得知那里出现了以上情况,我们可以借助core文件和gdb来辅助调试。步骤如下:

  1. ulimit -c unlimited,作用:取消对core文件大小的限制
  2. gcc a.c -o a -g,作用:加编译选项-g使程序具备调试信息
  3. ./a,作用:故意执行一遍该毛病程序,使其生成一个core文件
  4. gdb ./a core,作用:使用调试器gdb来调试程序example,并且使用core文件提供的信息快速定位其中出现段错误的地方
  5. 查看第4步出现的信息并虔诚地祈祷,人品好的话立刻gdb将立刻指出错误所在行

第五,复杂逻辑错误。如果以上办法都无法拯救你的代码,那要么你开始面壁和自责,当初为什么要留下这么一个烂摊子,要么开始硬着头皮,使用gdb单步慢慢调试你的代码,直到找到错误所在或者放弃为止。

最后的忠告,不要等到调试不出来的时候,才想起程序框架的设计多么重要,才想起可扩展性和兼容性的基本要求,才实现数据和接口的分离,才懊恼当初没去开火锅店而选择去改变世界

0 人点赞