array[i] = 1.0;
这并不是一种随意的选择:这段代码要求访问n元数组里的每个元素,下标从0到n-1。在这里所有循环控制都被放在一个f o r里,以递增顺序运行,并使用+ +的习惯形式做循环变量的更新。这样做还保证循环结束时下标变量的值是一个已知值,它刚刚超出数组里最后元素的位置。熟悉C语言的人不用琢磨就能理解它,不加思考就能正确地写出它来。
对于无穷循环,我们喜欢用:
for (; ;)...
但while(1)...
也很流行。请不要使用其他形式。
缩排也应该采用习惯形式。
一致地使用习惯用法还有另一个优点,那就是使非标准的循环很容易被注意到,这种情况常常预示着有什么问题:
1.4 函数宏
老的C语言程序员中有一种倾向,就是把很短的执行频繁的计算写成宏,而不是定义为函数。完成I / O的g e t c h a r,做字符测试的i s d i g i t都是得到官方认可的例子。人们这样做最根本的理由就是执行效率:宏可以避免函数调用的开销。实际上,即使是在C语言刚诞生时(那时的机器非常慢,函数调用的开销也特别大),这个论据也是很脆弱的,到今天它就更无足轻重了。有了新型的机器和编译程序,函数宏的缺点就远远超过它能带来的好处。
函数宏最常见的一个严重问题是:如果一个参数在定义中出现多次,它就可能被多次求值。如果调用时的实际参数带有副作用,结果就会产生一个难以捉摸的错误。
给宏的体和参数都加上括号。如果你真的要使用函数宏,那么请特别小心。宏是通过文本替换方式实现的:定义体里的参数被调用的实际参数替换,得到的结果再作为文本去替换原来的调用段。
C++ 提供的在线函数既避免了语法方面的麻烦,而且又可得到宏能够提供的执行效率,很适合用来定义那些设置或者提取一个值的短小函数。
1.5 神秘的数
神秘的数包括各种常数、数组的大小、字符位置、变换因子以及程序中出现的其他以文字形式写出的数值。
字形式写出的数值。
(1) 给神秘的数起个名字。作为一个指导原则,除了0和1之外,程序里出现的任何数大概都可以算是神秘的数,它们应该有自己的名字。在程序源代码里,一个具有原本形式的数对其本身的重要性或作用没提供任何指示性信息,它们也导致程序难以理解和修改。下面的片段摘自一个在2 4×8 0的终端屏幕上打印字母频率的直方图程序,由于其中存在一些神秘的数,程序的意义变得很不清楚:
在这段代码里包含许多数值,如2 0、2 1、2 2、2 3、2 7等等。它们应该是互相有关系的……但是……它们确实有关系吗?实际上,在这个程序里应该只有三个数是重要的:2 4是屏幕的行数;8 0是列数;还有2 6,它是字母表中的字母个数。但这些数在代码中都没出现,这就使上面那些数显得更神秘了。
通过给上面的计算中出现的各个数命名,我们可以把代码弄得更清楚些。我们发现,例如数字3是由( 8 0-1 ) / 2 6得到的,而l e t应该有2 6个项,而不是2 7个(这个超一( o ff - b y - o n e )错误可能是由于写程序的人把屏幕坐标当作从1开始而引起的)。通过一些简化后,我们得到的结果代码是:
现在,主循环到底做什么已经很清楚了:它是一个熟悉的从0到N L E T的循环,是一个对数据(数组)元素操作的循环。程序里对d r a w的调用也同样容易理解,因为像M A X R O W和M I N C O L这样的词提醒我们实际参数的顺序。更重要的是,现在我们已经很容易把这个程序修改为能够对付其他的屏幕大小或不同的数据了。数被揭掉了神秘的面纱,代码的意义也随之一目了然了。
把数定义为常数,不要定义为宏。C程序员的传统方式是用# d e f i n e行来对付神秘的数值。C语言预处理程序是一个强有力的工具,但是它又有些鲁莽。使用宏进行编程是一种很危险的方式,因为宏会在背地里改变程序的词法结构。我们应该让语言去做正确的工作 。
与此类似的还有另一个问题,那就是程序里许多上下文中经常出现的0。虽然编译系统会把它转换为适当类型,但是,如果我们把每个0的类型写得更明确更清楚,对读程序的人理解其作用是很有帮助的。例如,用(v o i d *) 0或N U L L表示C里的空指针值,用‘\ 0’而不是0表示字符串结尾的空字节。
利用语言去计算对象的大小。不要对任何数据类型使用显式写出来的大小。例如,我们应该用s i z e o f(i n t) 而不是2或者4。基于同样原因,写s i z e o f(a r r a y [ 0 ] ) 可能比s i z e o f(i n t) 更好,因为即使是数组的类型改变了,也没有什么东西需要改变。利用运算符s i z e o f常常可以很方便地避免为数组大小引进新名字。
1.6 注释
最好的注释是简洁地点明程序的突出特征,或是提供一种概观,帮助别人理解程序。注释应该提供那些不能一下子从代码中看到的东西,或者把那些散布在许多代码里的信
息收集到一起。当某些难以捉摸的事情出现时,注释可以帮助澄清情况。如果操作本身非常明了,重复谈论它们就是画蛇添足了。
否定性的东西很不好理解,应该尽量避免。
无论产生脱节的原因何在,注释与代码矛盾总会使人感到困惑。由于误把错误注释当真,常常使许多实际查错工作耽误了大量时间。所以,当你改变代码时,一定要注意保证其中的注释是准确的。
学生常被告之应该注释所有的内容。职业程序员也常被要求注释他们的所有代码。但是,应该看到,盲目遵守这些规则的结果却可能是丢掉了注释的真谛。注释是一种工具,它的作用就是帮助读者理解程序中的某些部分,而这些部分的意义不容易通过代码本身直接看到。我们应该尽可能地把代码写得容易理解。在这方面你做得越好,需要写的注释就越少。好的代码需要的注释远远少于差的代码。
1.7 为何对此费心
这里最关键的结论是:好风格应该成为一种习惯。如果你在开始写代码时就关心风格问题,如果你花时间去审视和改进它,你将会逐渐养成一种好的编程习惯。一旦这种习惯变成自动的东西,你的潜意识就会帮你照料许多细节问题,甚至你在工作压力下写出的代码也会更好。