作为程序员容易忽略的一些问题 读《Code Quality :The Open Source Perspective》有感

  由于IT行业的快速发展,软件的飞速开发,导致我们每天快速消化很多代码,每天也要产出很多代码。尤其是我这种基础没有打牢固,就接触软件开发的码农,编程中遇到很多bug,同时代码的质量也不高,杂乱而又冗余。而且存在很多隐患。最近读了《Code Quality :The Open Source Perspective》中文是《高质量程序设计艺术》一本书,此书的作者时资深的软件开发大师。虽说此书的很多内容由于自己水平有限没有看懂,但是有些平常不注意的缺陷在书中都有提及。

    感触颇深,单纯说虚的也不好,不如就举点里面简单的例子,来和大家一起分享下。

1. 如未初始化变量,可能会导致表达式中错误的操作数

for(int i=0;i<sizepg;i++)

{

if ( […])

{

[….]

addr=k;

break;

}

}

Pc->addr=addr;

有些编译器能够检测出未初始化的变量: Java 和c#的编译器会将未初始化的局部变量标记为错误,gcc则提供了 –Wuninitialized 选项,以在发现问题的时候给出警告。遗憾的是,编译器并不总是能够可靠地验证变量是否被初始化。 上面的代码就是一个很好的说明。这里只有第一个if语句的条件为真时,变量addr才被初始化。否则这个函数就没有初始化addr就提前退出了。

 

2. 一些错误的运算符

  if(( nodes != null)  &&  (nodes.size()>0)

        {[…]}

一些运算符在工作中犯错的情况不是很多,但是也有些常见的情况值得说说。在构造布尔表达式的时候,请记住逐位AND 和OR运算符并不能用在类似的布尔运算符所使用的场合,也就是说,不要用& 来替换&& ,也不要用|来替换||。 尽管结果是一样的,但是得到结果的过程会导致巨大不同。与进行懒求值(只对得到表达式的真值所必须的项计算)的布尔AND 和OR 运算符不同,逐位AND与OR 运算符对整个表达式做计算

上面的代码中如果使用了逐位AND运算符(&) 并且nodes 是null, 第2项仍然会运算,导致null-PointerException异常。

 

3. 运算符优先级问题

    请凭第一反应回答:在如下的表达式中,加法和左移操作哪一个先发生?

changes=(1<<GC+1)-1;

    除非你是这个星球上极少记住了c/c++/java 运算符的大约15个优先级的人之一,否则大约50%的概率会答错。正确的答案是加法先于左移操作。因此在统一表达式中混用不常用的运算符是,他们的优先级很容易被搞错,因此使用这些运算符的表达式都应该扩上括号,已明确优先级。

4.不正确的比例或数据单位

大多数程序将物理量作为未经处理的数来操作:

struct timeval

{long tv_sec;

long tv_usec;   };

   当使用不兼容数据单位的数被混用的时候,这可能会导致问题。例如1999年9月火星气象卫星失事的原因就是英制和公制的混用。离我们更近的是,两行代码都会暂停程序的运行1秒钟

sleep(1);

[...]

Sleep(1000)

     一个是调用的unix sleep 函数,而另一个是调用Win32 的Sleep函数,以毫秒最为单位。而且使用typedef 在c c++中声明合适的类型是不够的,因为typedef 只是给定的类型引入了一个别名,程序员还是可以自由混用不同类型,而编译器并不会认为这有什么问题。

   例如:

假定做了如下声明:

typedef int Linetype ;

[…]

typedef int Reject_level;

 

  我们还是可以将Reject_level 类型的值付给一个声明为Linetype的变量。避免这种问题的正确方法是将值封装进 struct 或 c++/java  class里面。

 

5.格式字符串漏洞

     很多输出例程都有一个特殊的参数,该参数是一个格式字符串,用来指定输出的格式。典型的例子有printf, fprintf, Unix 系统中特有的函数 syslog、warn等

     对这些例程的一种误用是将需要输出的字符串直接指定为格式字符串参数。

    如warn(name)

    这种用法的问题在于恶意构造的输出字符串可能包含格式指定符,例如:%s 或者%d。精心构造的字符串可以让程序去读或者写任意的内存位置,进而被用来获得对系统的控制权。

    修正这些问题还是很容易的,使用一个格式字符串作为第一个参数,这样原来的参数就会被格式化为一个字符串输出。

可以用

       Warn(%s,name);

6.表达式的一致性问题

Bp->y = rnd(LINES-3)+ 2;

写这段代码的程序员不是很明确该如何在双目运算符的两边放置空格,在“=”两边都有空格,在“-”两边没有空格,在“+”运算符则只有一边有一个空格。所有的c/c++与Java代码风格指南都明确指定双目运算符永远都应该被空格环绕。在括号,标识符和单目运算符的两边不需要格外的空格。

举个正确格式化的例子:

N = (time – diff) % (3600 * 24);

其实以上所举是平常很简单而且经常容易犯的错误,真正的高质量程序是要靠日积月累的。本书中作者全面讨论了各种具体的编程错误,读起来也颇有乐趣,而且也很实用。只恨自己水平有限,很多没有接触过。希望在实践中和读书中慢慢充实自己的编程质量。慢慢体会那编程艺术。(Sight is a faculty; seeing, an art)

by haifeng

posted @ 2011-10-28 00:47  OMG! 日记  阅读(2841)  评论(5编辑  收藏  举报