初学者阶段编程时,编写基本语句可能会有隐含错误的方式,基本语句主要针对if、for、while、goto、switch等,它们看似简单,但使用时隐患比较多,本文归纳了使用语句的一些规则和建议。
基本语句编程举例if语句
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)
(2)整型变量与零值比较:应当将整型变量用“==”或“!=”直接与0比较。假设整型变量的名字为value,它与零值比较的标准if语句如下:
if(value==0)if(value!=0)
不可以模仿布尔变量的风格而写成:
if(value)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)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)returnx;returny;
改写成
if(condition){returnx;}else{returny;}
或者改成更加简练的:
return(condition?x:y);
循环语句的效率
C++/C循环语句中,for语句使用频率最高,while语句其次,do语句很少用。提高循环体效率的基本方法是降低循环体的复杂性。
(1)在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数。例如下面代码示例b的效率就比示例a的高。
示例a:低效率(长循环在最外层)
for(row=0;row;row++){for(col=0;col5;col++){sum=sum+a[row][col];}}
示例b:高效率(长循环在最内层)
for(col=0;col5;col++){for(row=0;row;row++){sum=sum+a[row][col];}}
(2)如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。
示例c的程序比示例d多执行了N-1次逻辑判断。并且由于前者老要进行逻辑判断,打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。如果N非常大,最好采用示例d的写法,可以提高效率。如果N非常小,两者效率差别并不明显,采用示例c的写法比较好,因为程序更加简洁。
示例c:效率低但程序简洁
for(i=0;iN;i++){if(condition)DoSomthing();elseDoSomthing();}
示例d:效率高但程序不简洁
if(condition){for(i=0;iN;i++)DoSomething();}else{for(i=0;iN;i++)DoSomething();}
(3)for语句的循环控制变量
不可以在for循环体内修改循环变量,防止for循环失去控制。
建议for语句的循环控制变量的取值采用“半开半闭区间”写法。
示例e中的x值属于半开半闭区间“0=xN”,起点到终点的间隔为N,循环次数为N。示例f中的x值属于闭区间“0=x=N-1”,起点到终点的间隔为N-1,循环次数为N。相比之下,示例e的写法更加直观,尽管两者的功能是相同的。
示例e:循环变量属于半开半闭区间
for(intx=0;xN;x++){}
示例f:循环变量属于闭区间
for(intx=0;x=N-1;x++){}
switch语句switch是多分支选择语句,而if语句只有两个分支可供选择。虽然可以用嵌套的if语句来实现多分支选择,但那样的程序冗长难读。switch语句基本格式:
switch(variable){casevalue1:break;casevalue2:break;default: break;}
每个case语句的结尾不要忘了加break,否则将导致多个分支重叠(除非有意使多个分支重叠)。不要忘记最后那个default分支,即使程序真的不需要default处理,也应该保留语句default:break;这样做并非多此一举,而是为了防止别人误以为你忘了default处理。
goto语句
自从提倡结构化设计以来,goto就成了有争议的语句。
由于goto语句可以灵活跳转,如果不加限制,它的确会破坏结构化设计风格。
goto语句经常带来错误或隐患。它可能跳过了某些对象的构造、变量的初始化、重要的计算等语句,
例如:
gotostate;Strings1,s2;//被goto跳过intsum=0;//被goto跳过state:
如果编译器不能发觉此类错误,每用一次goto语句都可能留下隐患。很多人建议废除C++/C的goto语句,以绝后患。
但实事求是地说,错误是程序员自己造成的,不是goto的过错。goto语句至少有一处可显神通,它能从多重循环体中一下子跳到外面,用不着写很多次的break语句;例如:
{{{gotoerror;}}}error:
就像楼房着火了,来不及从楼梯一级一级往下走,可从窗口跳出火坑,所以我们主张少用、慎用goto语句,而不是禁用。
小结主要针对if、for、while、goto、switch等基本语句使用时可能出现隐患问题,归纳了正确使用它们的一些规则和建议。如有不对留言指正参考资料:林锐《c/c++编程指南》
最近文章推荐:
const关键字应用总结
Linux思维导图整理(建议收藏)
C语言C++中assert的用法
字符串操作的全面总结
代码防御性编程的十条技巧
九种查找算法
数据结构之堆栈
一文轻松理解内存对齐
点是最大的支持
自成一派赞赏诚可贵,在看转发价更高