少有成功的经验 不缺失败的教训

赵云30 发布于 2013/10/20 23:42
阅读 603
收藏 3

大家好.    标题所写是本人软件开发和测试职业生涯的一个总结, 下面具体谈谈, 主要谈失败的教训.

最重要的一点我认为是要在开始编程时就写测试程序. 特别是从头开发一个新东西时, 编写测试程序特别重要, 要开发人员编写测试程序 比专门成立一个测试小组来找Bug更能从根本上解决产品质量问题.

我参加工作的第一个单位就没有做到这点, 程序很乱, 一点测试程序都没有.  早先,我也觉得写测试程序浪费时间,  不过那是我在学生和刚参加工作不久时候的想法, 总觉得要学的东西太多, 没时间来写无用的测试程序. 现在慢慢意识到过去的想法是大错特错.

所谓的测试程序不是指非要专门编写单元测试函数, 对每个函数的输入输出返回值都测一遍, 更多是指编写程序的思路过程甚至帮助理解程序流程的printf打印输出都算, 这些调试的程序不应删掉, 而应该放在程序中...  新手容易犯的错误就是调试完成以后就把printf打印出来的信息删掉. 这是不好的, 很多时候程序运行的机器是不允许你上去调试的,没有这个环境, 假如程序崩溃了, 这些printf或者写到文件的信息就非常有用了.

除了程序运行过程中各种信息不能删(实在觉得没必要, 可以注释掉). 还有你测试某些函数的程序最好也不要删. 比如你写了大文件big.c, 里面有一个函数 char *test_big(int a, char *p),你在调用时发现这个函数没有按预期的工作, 于是你想办法了, 加printf调试信息,上gdb, 还是找不到问题出在哪里, 于是你把这段程序单独弄到一个文件中,写上main函数来调用这函数来找问题... 最后问题解决了, 你又把单独弄出来的程序又给删了. 

很不幸, 你又错了. 正确的做法,是把这些调试程序都保存下来,甚至提交到公司的代码库中.  我不知道有多人有过我这样的经历, 或许只是我编程的天分太差, 才会出现这么多让人崩溃的经历.

再一个经历是不要依赖gdb调试程序... 这东西可以学. 可以试试怎么用, 但是对培养编程能力害处更多!  慢慢的你的程序不是编制出来的,而是调出来的...  我这是什么意思呢?  当你知道一个程序的运行结果时, 你会尽量把程序往这个争取的这个结果上调, 而忽视了程序流程... 这就等于作弊.  再者, gdb调试程序我看不如printf打印各种关键的变量, 因为printf打印变量可以重定向到磁盘文件, 在实际的运行环境中程序崩溃了马上就能看文件能看出程序在哪里出错了. gdb做不到. 当然你可以把-g生成的程序拿去运行, 打开core dump, 程序崩溃了来分析coredump, 但我感觉这样做不比分析自己printf信息好,  因为printf信息是你自己写的, 映像会更深, printf信息你可以自己格式划输出, %s, %d, %c %x.....

我的这个经验对某些人似乎就不正确... Sun OS系统崩溃时会产生core文件, 假如某台机器panic了, Sun的开发人员会问你要产生的core文件拿去用mdb分析; Linux呢?我不知道, 他们是怎么解决核心panic问题的,可能也是通过调试器吧. 但他们的这种分析core文件的方式并不适合我,  同样也不适合很多其他人... 在我看来, 自己在程序里输出的信息要比gdb提供的信息更为可靠, 当然你在编写程序时就要做好程序可能不会正确工作的可能, 精心安排你的printf信息...

"我的程序怎么会崩溃? 我写的程序是完美无缺, 100%正确运行, 没有一点瑕疵的! " 这是许多初入职场的人容易犯的毛病, 我也是这样. 程序编的越多, 越明白程序出错是常态, 程序编制的奇丑无比是常态,尤其是小公司赶工忙着要完成客户的需要任务, 根本没时间让你去优化程序.  

完美的程序从来不会在机器上运行, 只会出现在图书中. 因为她们不需要运行,也就不可能出错, 或者只是一两句一两段一两页的小程序, 出错的概率小的多.

其实是,C/C++编程方面,  gcc要带上-Wall参数, 程序拿来编译,  满篇的warning.  很多人选择忽略警告,这是非常不好的, warning是潜在的Bug, 你应该一个一个的消灭. 等到满篇都是警告, 程序出现问题的时候, 你已经找不出问题所在了...  很可惜, 当时我所在的公司没有消灭掉warning, 我也随了大流, 往程序库里添加了一些带有warning的程序..



加载中
0
huan
huan
debug 不用gdb? 无语。。。。。。。。。。
0
赵云30
赵云30

引用来自“huan”的答案

debug 不用gdb? 无语。。。。。。。。。。

事实上,,gdb在复杂的程序面前是无能为力的,  只会把程序搞的更复杂, 问题更多.   最典型的就是多线程程序.  用gdb你也控制不了哪个线程执行或者挂起...更是无从调试, 他只能用些奇怪复杂的方式做那些printf能做而且做的很好的活..  这些, 没做多线程程序的人是不可能懂得.

0
flydom
flydom
gdb不如printf?gdb不能调试多线程?呵呵,不敢认同LZ观点。 正式的高质量的大型工程谁会在程序里留printf...?再者说,gdb是用来干printf的活么。。就算出错定位,那也是用断言,怎么可能用printf。还有,你说用gdb会控制流程走向?一个可能原因就是本身自己对流程,对业务逻辑不熟,一个对流程理解透彻才开始下手写代码的人,会出现向结果正确靠拢而忽视流程么?
0
赵云30
赵云30

引用来自“flydom”的答案

gdb不如printf?gdb不能调试多线程?呵呵,不敢认同LZ观点。 正式的高质量的大型工程谁会在程序里留printf...?再者说,gdb是用来干printf的活么。。就算出错定位,那也是用断言,怎么可能用printf。还有,你说用gdb会控制流程走向?一个可能原因就是本身自己对流程,对业务逻辑不熟,一个对流程理解透彻才开始下手写代码的人,会出现向结果正确靠拢而忽视流程么?

有些人已经走进了错误的深渊,走不出来了, 这些人不是我谈论的对象。 还断言,断言个屁。用户程序用printf或者类似的东西输出信息,Linux核心用printk打印信息,甚至专门建个/proc存放进程信息帮助用户来了解系统。

Solaris核心不知有有没有printk,就算没有,也是和printk类似的东西。 你再多找些项目看看, 这些大型工程可以一行你所谓的“断言”?

赵云30
赵云30
回复 @flydom : 断言就是assert,无非是一个宏。用不用关无所谓,虽然大项目没有用assert的,不过你喜欢用非要用我也不拦着你。
flydom
flydom
好吧,既然你只纠结断言这点。。那我请问LZ一句,你开发时,用过断言么?出错定位调试跟打印信息是一个概念么?另外,看的源码中确实很多printf和printk,我就想问,这些信息的打印是为了调试代码??我确实闲了。。
0
赵云30
赵云30

引用来自“长工”的答案

引用来自“flydom”的答案

gdb不如printf?gdb不能调试多线程?呵呵,不敢认同LZ观点。 正式的高质量的大型工程谁会在程序里留printf...?再者说,gdb是用来干printf的活么。。就算出错定位,那也是用断言,怎么可能用printf。还有,你说用gdb会控制流程走向?一个可能原因就是本身自己对流程,对业务逻辑不熟,一个对流程理解透彻才开始下手写代码的人,会出现向结果正确靠拢而忽视流程么?

有些人已经走进了错误的深渊,走不出来了, 这些人不是我谈论的对象。 还断言,断言个屁。用户程序用printf或者类似的东西输出信息,Linux核心用printk打印信息,甚至专门建个/proc存放进程信息帮助用户来了解系统。

Solaris核心不知有有没有printk,就算没有,也是和printk类似的东西。 你再多找些项目看看, 这些大型工程可以一行你所谓的“断言”?

回复 @flydom :    说是调试代码不太正确,更准确说是用来提示判断系统所处的状态,比如你开机时可能会看到磁盘信息,内存信息,挂载文件系统, 假如那段程序没写好,磁盘信息没出来,我就知道到底是哪里出错。要说这是为了调试程序也无可厚非。


当然, 我们见到的最终代码必然是删了不少无用的打印出来的信息的。这要有的权衡。再比如Apache会往log里写一大堆启动运行信息,Windows系统各种软件出错了可能会声称一个error.log文本文件。这些可读的文本文件比不可读的Coredump文件更容易“调试”,你甚至不用调试,看一眼就知道哪里出问题了。

我举printf只是举一个例子,事实上,你可以完全不用printf, 用write,fwrite等等类似的写文件函数,关键是要分清哪些该写出来,哪些可以删掉,这个工作本身就是你说的“业务逻辑”。 

返回顶部
顶部