|
初学者阶段编程时,编写基本语句可能会有隐含错误的方式,基本语句主要针对if、for、while、goto、switch等,它们看似简单,但使用时隐患比较多,本文归纳了使用语句的一些规则和建议。 基本语句编程举例
if语句是C++/C语言中最简单、最常用的语句,然而很多编程人员用隐含错误的方式写if语句,本文以“与零值比较”为例,进行讨论。 (1)布尔变量与零值比较:不可将布尔变量直接与TRUE、FALSE或者1、0比较。根据布尔类型的语义,零值为“假”(记为FALSE),任何非零值都是“真”(记为TRUE)。TRUE的值究竟是什么并没有统一的标准, 例如VC++将TRUE定义为1,而VB则将TRUE定义为-1。 假设布尔变量名为flag,它与零值比较的标准if语句如下 if(flag)//表示flag为真 if(!flag) //表示flag为假 其他的用法都属于不良风格,例如: if(flag == TRUE) if(flag == FALSE) if(flag == 1 ) if(flag == 0 )& {6 j e4 {/ T4 O; z' t3 e- M (2)整型变量与零值比较:应当将整型变量用“==”或“!=”直接与0比较。假设整型变量的名字为value,它与零值比较的标准if语句如下: if(value == 0) if(value != 0) 不可以模仿布尔变量的风格而写成: if(value)6 w3 A) c/ A) h! C& z if(!value) //会让人误解value是布尔变量 (3)浮点变量与零值比较:不可以将浮点变量用“==”或“!=”与任何数字比较。 千万留意,无论是float还是double类型的变量,都有精度限制,所以一定要避免将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。假设浮点变量的名字为x,应该将 if(x == 0.0) //隐含错误的比较 转化为 if((x >= -EPSINON) && (x <= EPSINON)) //其中EPSINON是允许的误差(即精度)。 (4)指针变量与零值比较:应当将指针变量用“==”或“!=”与NULL比较。 指针变量的零值是“空”(记NULL)。尽管NULL的值与0相同,但二者的意义不同。假设指针变量名p,它与零值比较的标准if语句如下: if(p == NULL) if(p != NULL) //p与NULL显式比较,强调p是指针变量 不要写成: if(p == 0)- Z) `; F! i+ k: K7 @$ O! j if(p != 0) //容易让人误解p是整型变量 或者 if(p) if(!p) //容易让人误解p是布尔变量 (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)( ]% L- s$ o# T- ? return x;& I5 G: |5 L+ G$ Q7 h return y; 改写成 if(condition). s4 J1 _5 k& f* Z3 _- h {4 u: W' k5 k7 v' i: n2 @ return x; }, m( L3 a; |# q8 e2 h. [ else {; s8 K* e. Q. J1 s return y; } 或者改成更加简练的: return(condition ?x:y);
C++/C循环语句中,for语句使用频率最高,while语句其次,do语句很少用。提高循环体效率的基本方法是降低循环体的复杂性。 (1)在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数。例如下面代码示例b的效率就比示例a的高。 示例a:低效率(长循环在最外层) for(row = 0; row < 100; row++) {) Z8 B. X. q% Y5 }! `8 |6 S% u for(col=0;col<5;col++) { sum = sum +a[row][col]; }# q3 L4 g' V! o! A: @$ k: r2 m } 示例b:高效率(长循环在最内层) for(col = 0; col < 5; col++) m( ]1 w& Y( {% e2 x# i2 [+ ^ {8 ] Z" c ~7 P; ]! T w for(row=0;row<100;row++)1 H$ Z1 I" f n; G {% i! f7 P U( Z; R sum = sum +a[row][col]; I, V% j/ [8 }; b8 x# y }! x$ a& H& G5 Z% y G. a& c9 x7 D } (2)如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。 示例c的程序比示例d多执行了 N-1 次逻辑判断。并且由于前者老要进行逻辑判断,打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。如果 N 非常大,最好采用示例 d的写法,可以提高效率。如果 N 非常小,两者效率差别并不明显,采用示例 c的写法比较好,因为程序更加简洁。 示例c:效率低但程序简洁 for(i = 0; i < N; i++) { if(condition) DoSomthing(); else DoSomthing(); } 示例d:效率高但程序不简洁 if (condition) { for (i=0; i<N; i++) DoSomething();( [0 K7 D$ f1 G }$ g9 ?/ A$ l' ^( R else x5 a& B' O8 t/ o: \ {4 e4 {7 a+ q7 g! E u for (i=0; i<N; i++); _9 A0 Y! k& @5 d: J M s) Q DoSomething(); } (3)for语句的循环控制变量 不可以在for循环体内修改循环变量,防止for循环失去控制。 建议for语句的循环控制变量的取值采用“半开半闭区间”写法。 示例e中的x值属于半开半闭区间“0=<x<N”,起点到终点的间隔为N,循环次数为N。示例f中的x值属于闭区间“0=<x<=N-1”,起点到终点的间隔为N-1,循环次数为N。相比之下,示例e的写法更加直观,尽管两者的功能是相同的。 示例e:循环变量属于半开半闭区间 for(int x = 0;x < N; x++)2 z! e- W, h. W4 a { } d- [& R% g7 Y2 b( U1 b 示例f:循环变量属于闭区间 for(int x = 0;x <= N-1; x++) { }
switch(variable) { _2 I, J* j8 k6 T+ o7 D6 V case value1: break; case value2:9 f% U9 i0 p7 U8 E( ? break;" v4 v: T( k2 g9 p default:. K( }, D" n7 N f9 u break; }' X0 E" {! z6 i+ i 每个 case 语句的结尾不要忘了加 break,否则将导致多个分支重叠(除非有意使多个分支重叠)。不要忘记最后那个 default 分支,即使程序真的不需要 default 处理,也应该保留语句 default : break; 这样做并非多此一举,而是为了防止别人误以为你忘了 default 处理。
自从提倡结构化设计以来,goto 就成了有争议的语句。
例如: goto state;- p' }7 q1 I5 }% X/ C$ _ String s1, s2; // 被 goto 跳过2 N2 g, x! i5 i8 a; h& | {. f2 U" G" S int sum = 0; // 被 goto 跳过0 u& s+ Z' f8 r0 A5 o state: 如果编译器不能发觉此类错误,每用一次 goto 语句都可能留下隐患。很多人建议废除 C++/C 的 goto 语句,以绝后患。 但实事求是地说,错误是程序员自己造成的,不是 goto 的过错。goto 语句至少有一处可显神通,它能从多重循环体中一下子跳到外面,用不着写很多次的 break 语句; 例如: {$ e8 K3 |% |; ]5 ^ { {; e$ Z' `/ Q* w0 J0 z goto error;" x, u/ n& D4 l7 j% f; x$ R c6 ^+ _ } }. y& _$ r& T, p( b1 c1 ? }! p: R: u- h, I# k& B0 t8 K8 ]1 E error: 就像楼房着火了,来不及从楼梯一级一级往下走,可从窗口跳出火坑,所以我们主张少用、慎用 goto 语句,而不是禁用。 小结主要针对if、for、while、goto、switch等基本语句使用时可能出现隐患问题,归纳了正确使用它们的一些规则和建议。如有不对留言指正 参考资料:林锐《 c/c++编程指南》 |
| 谢谢分享 |
微信公众号
手机版