高质量C++/C编程指南 页码,26/101 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 ;/ 复合表达式过于复杂 ● 【规则42-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.3if语句 if语句是C+/C语言中最简单、最常用的语句,然而很多程序员用隐含错误的方式写if语句。本节 以“与零值比较”为例,展开讨论。 4.3.1布尔变量与零值比较 ·【规则4-3-1】不可将布尔变量直接与TRUE、FALSE或者1、0进行比较。 根据布尔类型的语义,零值为“假”(记为FALSE),任何非零值都是“真”(记为TRUE)。 TRUE的值究竞是什么并没有统一的标准。例如Visual C+将TRUE定义为I,而Visual Basic则将TRUE 定义为-1。 假设布尔变量名字为flag,它与零值比较的标准f语句如下: if(f1ag)/表示f1ag为真 if(!flag)/表示f1ag为假 其它的用法都属于不良风格,例如: http://man.chinaunix.net/develop/c&c++/c/c.htm 2006-3-30
if ((a | b) && (a & c)) 4.2 复合表达式 如 a = b = c = 0 这样的表达 式称为复合表达 式。允许复合表达 式存在的理由是:(1)书写简洁; (2)可以提高编译效率。但要防止滥用复合表达 式。 l 【规则 4-2-1 】不要编写太复杂的复合表达 式。 例如: i = a >= b && c < d && c + f <= g + h ; // 复合表达 式过于复杂 l 【规则 4-2-2 】不要有多用途的复合表达 式。 例如: d = (a = b + c) + r ; 该表达式既求a值又求d值。应该拆分为两个独立的语句: a = b + c; d = a + r; l 【规则 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 布尔变 量与零值比较 l 【规则 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为假 其它的用法都属于不良风格,例如: 高质量C++/C编程指南 页码,26/101 http://man.chinaunix.net/develop/c&c++/c/c.htm 2006-3-30
高质量C++/C编程指南 页码,27/101 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】不可将浮点变量用“=”或“!=”与任何数字比较。 千万要留意,无论是f1oat还是double类型的变量,都有精度限制。所以一定要避免将浮点变量用 “=”或“!=”与数字比较,应该设法转化成“>=”或“(=”形式。 假设浮点变量的名字为x,应当将 if(x=0.0) //隐含错误的比较 转化为 if ((x>=-EPSINON)&(x<=EPSINON)) 其中EPSINON是允许的误差(即精度)。 4.3.4指针变量与零值比较 ●【规则43-4】应当将指针变量用“=”或!=”与NULL比较。 指针变量的零值是“空”(记为NULL)。尽管NULL的值与0相同,但是两者意义不同。假设指针变量 的名字为p,它与零值比较的标准f语句如下: 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)这样古怪的格式。不是程序写错了,是程序员为了防止 http://man.chinaunix.net/develop/c&c++/c/c.htm 2006-3-30
if (flag == TRUE) if (flag == 1 ) if (flag == FALSE) if (flag == 0) 4.3.2 整型变 量与零值比较 l 【规则 4-3-2 】应当将整型变量用“==”或“!=”直接与0比较。 假设整型变量的名字为value ,它与零值比较 的标准if语句如 下: if (value == 0) if (value != 0) 不可模仿布尔变量的风格而写成 if (value) // 会让人误解 value 是布尔变量 if (!value) 4.3.3 浮点变 量与零值比较 l 【规则 4-3-3 】不可将浮点变量用“==”或“!=”与任何数字比较。 千万要留意,无论是float 还是 double 类型的变量,都有精度 限制。所以一定要避免将浮点变 量用 “==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。 假设浮点变量的名字为x,应当 将 if (x == 0.0) // 隐含错误的 比较 转化为 if ((x>=-EPSINON) && (x<=EPSINON)) 其中EPSINON 是允许的误差(即精度)。 4.3.4 指针变 量与零值比较 l 【规则 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) 这样古怪的格式 。不 是程序 写错了,是程序 员为了 防止 高质量C++/C编程指南 页码,27/101 http://man.chinaunix.net/develop/c&c++/c/c.htm 2006-3-30
高质量C++/C编程指南 页码,28/101 将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-41】在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外 层,以减少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++) { sumsum +a[row][col]; sum sum a[row][col]; } ! 示例4-4(a)低效率:长循环在最外层 示例4-4(6)高效率:长循环在最内层 ● 【建议4-42】如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外 面。示例4-4(c)的程序比示例4-4(d)多执行了N-1次逻辑判断。并且由于前者老要进行逻辑判断, 打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。如果非常大,最 好采用示例4-4()的写法,可以提高效率。如果N非常小,两者效率差别并不明显,采用示例4-4 http://man.chinaunix.net/develop/c&c++/c/c.htm 2006-3-30
将 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语句 很少用。本节重点 论述循环体 的效率。提高循环体效率 的基本办法是降低循环体 的复杂性。 l 【建议 4-4-1 】在多重循环 中,如果有可能,应当 将最 长的循环放 在最 内层,最短的循环放 在最外 层,以减少CPU跨切循环层的次数。例如示例4-4(b) 的效率比 示例4-4(a) 的高。 示例4-4(a) 低效率:长循环 在最外 层 示例4-4(b) 高效率:长循环在最内层 l 【建议 4-4-2 】如果循环体 内存在逻辑 判断,并且 循环 次数很大,宜将逻辑 判断移到循环体 的外 面。示例 4-4(c) 的程序 比示例 4-4(d) 多执行了N-1次逻辑 判断。并且 由于前者 老要进行逻辑 判断, 打断了循环 “流水 线”作业,使得 编译器不能对循环 进行优化处理 ,降低了效率 。如果N非常大,最 好采用示例 4-4(d) 的写法,可以提 高效率 。如果N非常小,两者效率 差别并不明显,采用示例 4-4 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]; } } 高质量C++/C编程指南 页码,28/101 http://man.chinaunix.net/develop/c&c++/c/c.htm 2006-3-30
高质量C+/C编程指南 页码,29/101 (©)的写法比较好,因为程序更加简洁。 for(i=0:i<N:1++) if (condition) { { if (condition) for (i=0;i<N;i++) DoSomething(): DoSomething(): else } DoOtherthing(); else { for (i=0:i<N:i++) DoOtherthing(); 表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(6)循环变量属于闭区间 4.6 switch语句 有了if语句为什么还要switch语句? switch是多分支选择语句,而if语句只有两个分支可供选择。虽然可以用嵌套的if语句来实现多 分支选择,但那样的程序冗长难读。这是switcl语句存在的理由。 switch语句的基本格式是: switch (variable) case valuel:… http://man.chinaunix.net/develop/c&c++/c/c.htm 2006-3-30
(c)的写法比较好,因为程序更 加简洁。 表4-4(c) 效率低但程序简 洁 表4-4(d) 效率高但程序不简洁 4.5 for 语句的循环控制变 量 l 【规则 4-5-1 】不可在for 循环体 内修改 循环变 量,防止for 循环失去控制。 l 【建议 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) 循环变 量属于半开半闭区间 示例4-5(b) 循环变 量属于闭区间 4.6 switch 语句 有了if语句为什么还要 switch 语句? switch 是多分支选择 语句 ,而if语句 只有两个 分支可供选择 。虽然可以用嵌套的if语句 来实现 多 分支选择 ,但那样的程序 冗长难读。这 是switch 语句存 在的理由。 switch 语句的基本格式 是: switch (variable) { case value1 : … 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(); } for (int x=0; x<N; x++) { … } for (int x=0; x<=N-1; x++) { … } 高质量C++/C编程指南 页码,29/101 http://man.chinaunix.net/develop/c&c++/c/c.htm 2006-3-30
高质量C++/C编程指南 页码,30/101 break; case value2 break; default break; ● 【规则4-6-1】每个case语句的结尾不要忘了加break,否则将导致多个分支重叠(除非有意使多 个分支重叠)。 ● 【规则4-6-2】不要忘记最后那个default分支。即使程序真的不需要default处理,也应该保留语 公 default:break;这样做并非多此一举,而是为了防止别人误以为你忘了default处理。 4.7goto语句 自从提倡结构化设计以来,goto就成了有争议的语句。首先,由于goto语句可以灵活跳转,如果 不加限制,它的确会破坏结构化设计风格。其次,g0t0语句经常带来错误或隐患。它可能跳过了某些 对象的构造、变量的初始化、重要的计算等语句,例如: goto state; String s1,s2;//被goto跳过 int sum=0://被goto跳过 state: 如果编译器不能发觉此类错误,每用一次goto语句都可能留下隐患。 很多人建议废除C+/C的g0t0语句,以绝后患。但实事求是地说,错误是程序员自己造成的,不是 goto的过错。goto语句至少有一处可显神通,它能从多重循环体中咻地一下子跳到外面,用不着写很 多次的break语句:例如 {… {… { goto error; } error: 就象楼房着火了,来不及从楼梯一级一级往下走,可从窗口跳出火坑。所以我们主张少用、慎用 goto语句,而不是禁用。 http://man.chinaunix.net/develop/c&c++/c/c.htm 2006-3-30
break; case value2 : … break; … default : … break; } l 【规则 4-6-1 】每个case 语句 的结 尾不要忘了加break ,否则将导致多个 分支重叠(除非有意使多 个分支重叠)。 l 【规则 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语句,而不是禁用。 高质量C++/C编程指南 页码,30/101 http://man.chinaunix.net/develop/c&c++/c/c.htm 2006-3-30