写点什么

C 语言 /C++ 基本语句编程风格

发布于: 2020 年 09 月 26 日

初学者阶段编程时,编写基本语句可能会有隐含错误的方式,基本语句主要针对 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)    return x;return y;
复制代码

改写成

if(condition){    return x;}else{    return y;}
复制代码

或者改成更加简练的:

return(condition ?x:y);
复制代码
  • 循环语句的效率

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

(1)在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少 CPU 跨切循环层的次数。例如下面代码示例 b 的效率就比示例 a 的高。

示例 a:低效率(长循环在最外层)

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

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

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

(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();}else{    for (i=0; i<N; i++)      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++){}
复制代码

示例 f:循环变量属于闭区间

for(int x = 0;x <= N-1; x++){}
复制代码

switch 语句 switch 是多分支选择语句,而 if 语句只有两个分支可供选择。虽然可以用嵌套的 if 语句来实现多分支选择,但那样的程序冗长难读。switch 语句基本格式:

switch(variable){    case value1:                  break;    case value2:                  break;        default:		  break;}
复制代码

每个 case 语句的结尾不要忘了加 break,否则将导致多个分支重叠(除非有意使多个分支重叠)。不要忘记最后那个 default 分支,即使程序真的不需要 default 处理,也应该保留语句 default : break; 这样做并非多此一举,而是为了防止别人误以为你忘了 default 处理。

  • 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 语句,而不是禁用。

小结

主要针对 if、for、while、goto、switch 等基本语句使用时可能出现隐患问题,归纳了正确使用它们的一些规则和建议。如有不对留言指正

参考资料:林锐《 c/c++编程指南》

最近文章推荐

const关键字应用总结

Linux思维导图整理(建议收藏)

C语言C++中assert的用法

字符串操作的全面总结

代码防御性编程的十条技巧

 九种查找算法

  数据结构之堆栈

 一文轻松理解内存对齐



发布于: 2020 年 09 月 26 日阅读数: 55
用户头像

欢迎关注微信公众号:C语言与CPP编程 2020.09.02 加入

欢迎关注微信公众号:C语言与CPP编程

评论

发布
暂无评论
C语言/C++基本语句编程风格