IT学习者 | 文章大全 | 技术文档 | 桌面壁纸 | 实用查询 | 网络电台 | 成语 | 歇后语 | 网址 | 下载 | 周公解梦 | 生日密码 | 电视剧365 | Flash
 您现在的位置: IT学习者 >> 文章大全 >> 网络编程 >> C++/C编程

高质量C++/C编程指南(四)

【 作者:林锐    来源:网络  更新时间:2006-1-11 | 字体:

第4章 表达式和基本语句

读者可能怀疑:连if、for、while、goto、switch这样简单的东西也要探讨编程风格,是不是小题大做?

我真的发觉很多程序员用隐含错误的方式写表达式和基本语句,我自己也犯过类似的错误。

表达式和语句都属于C++/C的短语结构语法。它们看似简单,但使用时隐患比较多。本章归纳了正确使用表达式和语句的一些规则与建议。

4.1 运算符的优先级

C++/C语言的运算符有数十个,运算符的优先级与结合律如表4-1所示。注意一元运算符 +  -  * 的优先级高于对应的二元运算符。

表4-1 运算符的优先级与结合律

优先级 运算符 结合律
( )  [ ]  ->  . 从左至右
从 !~++--类型)sizeof+-*& 从右至左
 *  /  % 从左至右
高 +  - 从左至右
  <<  >> 从左至右
到 <   <=   >  >= 从左至右
  ==  != 从左至右
低 & 从左至右
  ^ 从左至右
排 | 从左至右
&& 从左至右
列 || 从右至左
?: 从右至左
   =  +=  -=  *=  /=  %=  &=  ^=|=  <<=  >>= 从左至右

【规则4-1-1】如果代码行中的运算符比较多,用括号确定表达式的操作顺序,避免使用默认的优先级。

由于将表4-1熟记是比较困难的,为了防止产生歧义并提高可读性,应当用括号确定表达式的操作顺序。例如:

word = (high << 8) | low

if ((a | b) && (a & c))  

4.2 复合表达式

如 a = b = c = 0这样的表达式称为复合表达式。允许复合表达式存在的理由是:(1)书写简洁;(2)可以提高编译效率。但要防止滥用复合表达式。

【规则4-2-1】不要编写太复杂的复合表达式。

例如:
      i = a >= b && c < d && c + f <= g + h ;   // 复合表达式过于复杂

【规则4-2-2】不要有多用途的复合表达式。

例如: d = (a = b + c) + r ;
该表达式既求a值又求d值。应该拆分为两个独立的语句:
a = b + c;
d = a + r;

【规则4-2-3】不要把程序中的复合表达式与“真正的数学表达式”混淆。

例如: 
if (a < b < c)            // a < b < c是数学表达式而不是程序表达式
并不表示      
if ((a<b) && (b<c))
而是成了令人费解的
if ( (a<b)<c )

4.3 if 语句

 if语句是C++/C语言中最简单、最常用的语句,然而很多程序员用隐含错误的方式写if语句。本节以“与零值比较”为例,展开讨论。

4.3.1 布尔变量与零值比较

【规则4-3-1】不可将布尔变量直接与TRUE、FALSE或者1、0进行比较。

根据布尔类型的语义,零值为“假”(记为FALSE),任何非零值都是“真”(记为TRUE)。TRUE的值究竟是什么并没有统一的标准。例如Visual C++ 将TRUE定义为1,而Visual Basic则将TRUE定义为-1。

假设布尔变量名字为flag,它与零值比较的标准if语句如下:

if (flag)    // 表示flag为真
if (!flag)    // 表示flag为假

其它的用法都属于不良风格,例如:
    if (flag == TRUE)  
    if (flag == 1 )    
    if (flag == FALSE) 
    if (flag == 0)     

4.3.2 整型变量与零值比较

【规则4-3-2】应当将整型变量用“==”或“!=”直接与0比较。

假设整型变量的名字为value,它与零值比较的标准if语句如下:

if (value == 0) 
if (value != 0)

不可模仿布尔变量的风格而写成
if (value)    // 会让人误解 value是布尔变量
if (!value)

4.3.3 浮点变量与零值比较

【规则4-3-3】不可将浮点变量用“==”或“!=”与任何数字比较。

 千万要留意,无论是float还是double类型的变量,都有精度限制。所以一定要避免将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。
 假设浮点变量的名字为x,应当将  

if (x == 0.0)     // 隐含错误的比较
转化为
if ((x>=-EPSINON) && (x<=EPSINON))

其中EPSINON是允许的误差(即精度)。

4.3.4 指针变量与零值比较

【规则4-3-4】应当将指针变量用“==”或“!=”与NULL比较。

    指针变量的零值是“空”(记为NULL)。尽管NULL的值与0相同,但是两者意义不同。假设指针变量的名字为p,它与零值比较的标准if语句如下:
        if (p == NULL)    // p与NULL显式比较,强调p是指针变量
        if (p != NULL) 
    不要写成
        if (p == 0)   // 容易让人误解p是整型变量
        if (p != 0)   
    或者
    if (p)            // 容易让人误解p是布尔变量
        if (!p)           

4.3.5 对if语句的补充说明

有时候我们可能会看到 if (NULL == p) 这样古怪的格式。不是程序写错了,是程序员为了防止将 if (p == NULL) 误写成 if (p = NULL),而有意把p和NULL颠倒。编译器认为 if (p = NULL) 是合法的,但是会指出 if (NULL = p)是错误的,因为NULL不能被赋值。

程序中有时会遇到if/else/return的组合,应该将如下不良风格的程序

    if (condition)
        return x;
    return y;
改写为
    if (condition)
    {
        return x;
    }
    else
    {
return y;
}

或者改写成更加简练的

return (condition ? x : y);

4.4 循环语句的效率

    C++/C循环语句中,for语句使用频率最高,while语句其次,do语句很少用。本节重点论述循环体的效率。提高循环体效率的基本办法是降低循环体的复杂性。

【建议4-4-1】在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数。例如示例4-4(b)的效率比示例4-4(a)的高。
 
*示例4-4(a) 低效率:长循环在最外层

for (row=0; row<100; row++)
{
for ( col=0; col<5; col++ )
{
sum = sum + a[row][col];
}
}

*示例4-4(b) 高效率:长循环在最内层

for (col=0; col<5; col++ )
{
for (row=0; row<100; row++)
{
     sum = sum + a[row][col];
}
}


##【建议4-4-2】如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。示例4-4(c)的程序比示例4-4(d)多执行了N-1次逻辑判断。并且由于前者老要进行逻辑判断,打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。如果N非常大,最好采用示例4-4(d)的写法,可以提高效率。如果N非常小,两者效率差别并不明显,采用示例4-4(c)的写法比较好,因为程序更加简洁。

表4-4(c) 效率低但程序简洁
for (i=0; i<N; i++)
{
if (condition)
    DoSomething();
else
    DoOtherthing();
}

表4-4(d) 效率高但程序不简洁

if (condition)
{
for (i=0; i<N; i++)
    DoSomething();
}
else
{
    for (i=0; i<N; i++)
    DoOtherthing();
}

4.5 for 语句的循环控制变量

【规则4-5-1】不可在for 循环体内修改循环变量,防止for 循环失去控制。

【建议4-5-1】建议for语句的循环控制变量的取值采用“半开半闭区间”写法。

示例4-5(a)中的x值属于半开半闭区间“0 =< x < N”,起点到终点的间隔为N,循环次数为N。
示例4-5(b)中的x值属于闭区间“0 =< x <= N-1”,起点到终点的间隔为N-1,循环次数为N。
相比之下,示例4-5(a)的写法更加直观,尽管两者的功能是相同的。

示例4-5(a) 循环变量属于半开半闭区间 
for (int x=0; x<N; x++)
{

}

示例4-5(b) 循环变量属于闭区间
for (int x=0; x<=N-1; x++)
{

}

4.6 switch语句

   有了if语句为什么还要switch语句?
   switch是多分支选择语句,而if语句只有两个分支可供选择。虽然可以用嵌套的if语句来实现多分支选择,但那样的程序冗长难读。这是switch语句存在的理由。
   switch语句的基本格式是:

switch (variable)
{
case value1 :   …
break;
case value2 :   …
break;
    …
default :    …
break;
}

【规则4-6-1】每个case语句的结尾不要忘了加break,否则将导致多个分支重叠(除非有意使多个分支重叠)。

【规则4-6-2】不要忘记最后那个default分支。即使程序真的不需要default处理,也应该保留语句    default : break; 这样做并非多此一举,而是为了防止别人误以为你忘了default处理。

4.7 goto语句

自从提倡结构化设计以来,goto就成了有争议的语句。首先,由于goto语句可以灵活跳转,如果不加限制,它的确会破坏结构化设计风格。其次,goto语句经常带来错误或隐患。它可能跳过了某些对象的构造、变量的初始化、重要的计算等语句,例如:

goto state;
String s1, s2; // 被goto跳过
int sum = 0;  // 被goto跳过

state:


如果编译器不能发觉此类错误,每用一次goto语句都可能留下隐患。

很多人建议废除C++/C的goto语句,以绝后患。但实事求是地说,错误是程序员自己造成的,不是goto的过错。goto 语句至少有一处可显神通,它能从多重循环体中咻地一下子跳到外面,用不着写很多次的break语句; 例如

  { …
      { …
        { …
            goto error;
        }
      }
  }
  error:
  …

就象楼房着火了,来不及从楼梯一级一级往下走,可从窗口跳出火坑。所以我们主张少用、慎用goto语句,而不是禁用。

相 关 文 章
相 关 软 件
逃生 放生 黄玫瑰 想太多 那滋味 擦肩而过 放手去爱 北京欢迎你 依然在一起 吻得太逼真 感动天感动地 坐上火车去拉萨 怎么会狠心伤害我
心碎 冲动 小太阳 别碰我 蒲公英 千山万水 改变自己 一定要爱你 等爱的玫瑰 陷入爱里面 北极星的眼泪 最后一次的温柔 亲爱的那不是爱情
光荣 火花 坏女人 日不落 樱花草 为你写诗 独家记忆 夏天的味道 寂寞才说爱 忘不掉的伤 爱上你是个错 第三者的第三者 地球人都知道我爱你
假如 相思 是非题 有缘人 舍不得 我的答铃 死而无憾 外滩十八号 越爱越难过 123木头人 和寂寞说分手 爱上你是我的错 爱情里没有谁对谁错
加入收藏留言建议自助友情链接普通友情链接站长的Blog
版权所有   COPYRIGHT 2002-2008 ★IT学习者★ ALL RIGHTS RESERVED.