化境编程界推荐图书系列·高质量C艹C编程指南,ⅴ1.0 第4章表达式和基本语句 读者可能怀疑:连if、for、 while、goto、 switch这样简单的东西也要探讨编程风 格,是不是小题大做? 我真的发觉很多程序员用隐含错误的方式写表达式和基本语句,我自己也犯过类似 的错误 表达式和语句都属于C++/C的短语结构语法。它们看似简单,但使用时隐患比较多。 本章归纳了正确使用表达式和语句的一些规则与建议。 41运算符的优先级 C++/C语言的运算符有数十个,运算符的优先级与结合律如表4-1所示。注意一元 运算符+-*的优先级高于对应的二元运算符 优 先级 运算符 结合律 从左至右 (类型) sizeof 从右至左 从 & 晑 从从从 左左左 至至至 右右右 到 >> 从左至右 从左至右 低 从左至右 从左至右 列 从从从从从 左左右右左 至至至至至 右右左左右 /=%=& 表4-1运算符的优先级与结合律 【规则411】如果代码行中的运算符比较多,用括号确定表达式的操作顺序,避免 使用默认的优先级。 由于将表4-1熟记是比较困难的,为了防止产生歧义并提高可读性,应当用括号确 定表达式的操作顺序。例如 word=(high << 8) low
化境编程界推荐图书系列 - 高质量 C++/C 编程指南,v 1.0 2001 Page 26 of 101 第 4 章 表达式和基本语句 读者可能怀疑:连 if、for、while、goto、switch 这样简单的东西也要探讨编程风 格,是不是小题大做? 我真的发觉很多程序员用隐含错误的方式写表达式和基本语句,我自己也犯过类似 的错误。 表达式和语句都属于 C++/C 的短语结构语法。它们看似简单,但使用时隐患比较多。 本章归纳了正确使用表达式和语句的一些规则与建议。 4.1 运算符的优先级 C++/C 语言的运算符有数十个,运算符的优先级与结合律如表 4-1 所示。注意一元 运算符 + - * 的优先级高于对应的二元运算符。 优先级 运算符 结合律 ( ) [ ] -> . 从左至右 ! ~ ++ -- (类型) sizeof + - * & 从右至左 * / % 从左至右 + - 从左至右 << >> 从左至右 < <= > >= 从左至右 == != 从左至右 & 从左至右 ^ 从左至右 | 从左至右 && 从左至右 || 从右至左 ?: 从右至左 从 高 到 低 排 列 = += -= *= /= %= &= ^= |= <<= >>= 从左至右 表 4-1 运算符的优先级与结合律 z 【规则 4-1-1】如果代码行中的运算符比较多,用括号确定表达式的操作顺序,避免 使用默认的优先级。 由于将表 4-1 熟记是比较困难的,为了防止产生歧义并提高可读性,应当用括号确 定表达式的操作顺序。例如: word = (high << 8) | low
化境编程界推荐图书系列·高质量C艹C编程指南,ⅴ1.0 if((a b)&&(a& c)) 42复合表达式 如a=b=c=0这样的表达式称为复合表达式。允许复合表达式存在的理由是:(1) 书写简洁:(2)可以提高编译效率。但要防止滥用复合表达式。 【规则4-2-1】不要编写太复杂的复合表达式。 例如: i=a〉=b&&c<d&&c+f<=g+h;//复合表达式过于复杂 【规则42-2】不要有多用途的复合表达式。 例如: 该表达式既求a值又求d值。应该拆分为两个独立的语句 a =b+c ●【规则4-2-3】不要把程序中的复合表达式与“真正的数学表达式”混淆。 例如: f(a< b< c) /a<b<c是数学表达式而不是程序表达式 并不表示 f((a<b)&(b<c)) 而是成了令人费解的 if ((a<b)<c 4.3if语句 f语句是C++/C语言中最简单、最常用的语句,然而很多程序员用隐含错误的方式 写if语句。本节以“与零值比较”为例,展开讨论。 4.3.1布尔变量与零值比较 ●【规则4-3-1】不可将布尔变量直接与TRUE、 FALSE或者1、0进行比较。 根据布尔类型的语义,零值为“假”(记为 FALSE),任何非零值都是“真”(记为 TRUE)。TRUE的值究竟是什么并没有统一的标准。例如 Visual c++将TRUE定义为1 而Ⅴ isual basic则将TRUE定义为-1 假设布尔变量名字为flag,它与零值比较的标准if语句如下 if(flag)//表示flag为真 Page 27 of 101
化境编程界推荐图书系列 - 高质量 C++/C 编程指南,v 1.0 2001 Page 27 of 101 if ((a | b) && (a & c)) 4.2 复合表达式 如 a = b = c = 0 这样的表达式称为复合表达式。允许复合表达式存在的理由是:(1) 书写简洁;(2)可以提高编译效率。但要防止滥用复合表达式。 z 【规则 4-2-1】不要编写太复杂的复合表达式。 例如: i = a >= b && c < d && c + f <= g + h ; // 复合表达式过于复杂 z 【规则 4-2-2】不要有多用途的复合表达式。 例如: d = (a = b + c) + r ; 该表达式既求 a 值又求 d 值。应该拆分为两个独立的语句: a = b + c; d = a + r; z 【规则 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 布尔变量与零值比较 z 【规则 4-3-1】不可将布尔变量直接与 TRUE、FALSE 或者 1、0 进行比较。 根据布尔类型的语义,零值为“假”(记为 FALSE),任何非零值都是“真”(记为 TRUE)。TRUE 的值究竟是什么并没有统一的标准。例如 Visual C++ 将 TRUE 定义为 1, 而 Visual Basic 则将 TRUE 定义为-1。 假设布尔变量名字为 flag,它与零值比较的标准 if 语句如下: if (flag) // 表示 flag 为真
化境编程界推荐图书系列·高质量C艹C编程指南,ⅴ1.0 f(!flag)//表示flag为假 其它的用法都属于不良风格,例如 TRUE) if (flag = False) f (flag = 0) 4.3.2整型变量与零值比较 ●【规则4-3-2】应当将整型变量用“==”或“!=”直接与0比较 假设整型变量的名字为vale,它与零值比较的标准if语句如下: 不可模仿布尔变量的风格而写成 ( value)//会让人误解 value是布尔变量 4.3.3浮点变量与零值比较 【规则43-3】不可将浮点变量用“==”或“!=”与任何数字比较 千万要留意,无论是 float还是 double类型的变量,都有精度限制。所以一定要避 免将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。 假设浮点变量的名字为x,应当将 if(x==0.0)//隐含错误的比较 转化为 if ((x)=-EPSINON)&&(X<-EPSINON)) 其中 EPSINON是允许的误差(即精度) 4.3.4指针变量与零值比较 【规则43-4】应当将指针变量用“==”或“!=”与NULL比较 指针变量的零值是“空”(记为NULL)。尽管NULL的值与0相同,但是两者意义不 同。假设指针变量的名字为p,它与零值比较的标准if语句如下: if(p==NUL)∥/p与NUL显式比较,强调p是指针变量 if (p != NULL) 不要写成 if(p==0)//容易让人误解p是整型变量 或者 /容易让人误解p是布尔变量 if(!p 4.3.5对if语句的补充说明
化境编程界推荐图书系列 - 高质量 C++/C 编程指南,v 1.0 2001 Page 28 of 101 if (!flag) // 表示 flag 为假 其它的用法都属于不良风格,例如: if (flag == TRUE) if (flag == 1 ) if (flag == FALSE) if (flag == 0) 4.3.2 整型变量与零值比较 z 【规则 4-3-2】应当将整型变量用“==”或“!=”直接与 0 比较。 假设整型变量的名字为 value,它与零值比较的标准 if 语句如下: if (value == 0) if (value != 0) 不可模仿布尔变量的风格而写成 if (value) // 会让人误解 value 是布尔变量 if (!value) 4.3.3 浮点变量与零值比较 z 【规则 4-3-3】不可将浮点变量用“==”或“!=”与任何数字比较。 千万要留意,无论是 float 还是 double 类型的变量,都有精度限制。所以一定要避 免将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。 假设浮点变量的名字为 x,应当将 if (x == 0.0) // 隐含错误的比较 转化为 if ((x>=-EPSINON) && (x<=EPSINON)) 其中 EPSINON 是允许的误差(即精度)。 4.3.4 指针变量与零值比较 z 【规则 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 语句的补充说明
化境编程界推荐图书系列·高质量C艹C编程指南,ⅴ1.0 有时候我们可能会看到if(NUL=p)这样古怪的格式。不是程序写错了,是程 序员为了防止将if(p=NUL)误写成if(p=NUL),而有意把p和NUL颠倒。编 译器认为if(p=NUL)是合法的,但是会指出if(NULL=p)是错误的,因为NULL 不能被赋值 程序中有时会遇到if/else/ return的组合,应该将如下不良风格的程序 eturn 改写为 return el return y 或者改写成更加简练的 return (condition ? x: y) 44循环语句的效率 C++/C循环语句中,for语句使用频率最高, while语句其次,do语句很少用。本节 重点论述循环体的效率。提高循环体效率的基本办法是降低循环体的复杂性 ●【建议4-4-1】在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的 循环放在最外层,以减少CPU跨切循环层的次数。例如示例4-4(b)的效率比示例 4-4(a)的高。 for(row=0; row<100; row++) for(col=0; col<5: col++) for( col=0; col<5; col++ for(row=0; row<100; row++ sum= sum alrowcoll sum= sum+ arow[]; 示例4-4(a)低效率:长循环在最外层 例4-4(b)高效率:长循环在最内层 ●【建议4-4-2】如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到 Page 29 of 101
化境编程界推荐图书系列 - 高质量 C++/C 编程指南,v 1.0 2001 Page 29 of 101 有时候我们可能会看到 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 语句很少用。本节 重点论述循环体的效率。提高循环体效率的基本办法是降低循环体的复杂性。 z 【建议 4-4-1】在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的 循环放在最外层,以减少 CPU 跨切循环层的次数。例如示例 4-4(b)的效率比示例 4-4(a)的高。 for (row=0; row<100; row++) { for ( col=0; col<5; col++ ) { sum = sum + a[row][col]; } } for (col=0; col<5; col++ ) { for (row=0; row<100; row++) { sum = sum + a[row][col]; } } 示例 4-4(a) 低效率:长循环在最外层 示例 4-4(b) 高效率:长循环在最内层 z 【建议 4-4-2】如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到
化境编程界推荐图书系列·高质量C艹C编程指南,ⅴ1.0 循环体的外面。示例4-4(c)的程序比示例4-4(d)多执行了N-1次逻辑判断。并且由 于前者老要进行逻辑判断,打断了循环“流水线”作业,使得编译器不能对循环进 行优化处理,降低了效率。如果N非常大,最好采用示例4-4(d)的写法,可以提高 效率。如果N非常小,两者效率差别并不明显,采用示例4-4(c)的写法比较好,因 为程序更加简洁。 for (i=0; i<N: i++) f (condition) if (condition) for (i=0: i<N: i++) DoSomething o DoOtherthing o Otherthing o 表4-4(c)效率低但程序简洁 表4-4(d)效率高但程序不简洁 4.5for语句的循环控制变量 【规则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)的写法更加直观,尽管两者的功能是相同的。 for (int x=0: x<N: X++ for (int x=0: x<=N-1: x++) 示例4-5(a)循环变量属于半开半闭区间 示例4-5(b)循环变量属于闭区间 46 switch语句 有了if语句为什么还要 switch语句? age 30 of 101
化境编程界推荐图书系列 - 高质量 C++/C 编程指南,v 1.0 2001 Page 30 of 101 循环体的外面。示例 4-4(c)的程序比示例 4-4(d)多执行了 N-1 次逻辑判断。并且由 于前者老要进行逻辑判断,打断了循环“流水线”作业,使得编译器不能对循环进 行优化处理,降低了效率。如果 N 非常大,最好采用示例 4-4(d)的写法,可以提高 效率。如果 N 非常小,两者效率差别并不明显,采用示例 4-4(c)的写法比较好,因 为程序更加简洁。 for (i=0; i<N; i++) { if (condition) DoSomething(); else DoOtherthing(); } if (condition) { for (i=0; i<N; i++) DoSomething(); } else { for (i=0; i<N; i++) DoOtherthing(); } 表 4-4(c) 效率低但程序简洁 表 4-4(d) 效率高但程序不简洁 4.5 for 语句的循环控制变量 z 【规则 4-5-1】不可在 for 循环体内修改循环变量,防止 for 循环失去控制。 z 【建议 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)的写法更加直观,尽管两者的功能是相同的。 for (int x=0; x<N; x++) { … } for (int x=0; x<=N-1; x++) { … } 示例 4-5(a) 循环变量属于半开半闭区间 示例 4-5(b) 循环变量属于闭区间 4.6 switch 语句 有了 if 语句为什么还要 switch 语句?