你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

归纳了C语言/C++使用语句的一些规则和建议

[复制链接]
gaosmile 发布时间:2020-6-9 19:23

初学者阶段编程时,编写基本语句可能会有隐含错误的方式,基本语句主要针对if、for、while、goto、switch等,它们看似简单,但使用时隐患比较多,本文归纳了使用语句的一些规则和建议。

基本语句编程举例
  • if语句

    ( D4 N: f! p( `' n3 u

if语句是C++/C语言中最简单、最常用的语句,然而很多编程人员用隐含错误的方式写if语句,本文以“与零值比较”为例,进行讨论。

(1)布尔变量与零值比较:不可将布尔变量直接与TRUE、FALSE或者1、0比较。根据布尔类型的语义,零值为“假”(记为FALSE),任何非零值都是“真”(记为TRUE)。TRUE的值究竟是什么并没有统一的标准,

例如VC++将TRUE定义为1,而VB则将TRUE定义为-1。

假设布尔变量名为flag,它与零值比较的标准if语句如下

if(flag)//表示flag为真
9 M! K! b3 B* p. b/ Y& uif(!flag) //表示flag为假

9 j5 d- j  K: i  r

其他的用法都属于不良风格,例如:

if(flag == TRUE)
( T! G" K+ Y- j% I6 zif(flag == FALSE)
, J+ W4 B* b$ kif(flag == 1 )
4 ]) q8 ]$ D3 w) \; Gif(flag == 0 )
& {6 j  e4 {/ T4 O; z' t3 e- M

(2)整型变量与零值比较:应当将整型变量用“==”或“!=”直接与0比较。假设整型变量的名字为value,它与零值比较的标准if语句如下:

if(value == 0)
2 N2 w  o4 y6 {, [if(value != 0)

+ l9 i  t& s& Q

不可以模仿布尔变量的风格而写成:

if(value)6 w3 A) c/ A) h! C& z
if(!value)   //会让人误解value是布尔变量

  ]9 P  Y0 B  f% u6 Z# L4 L7 G  g

(3)浮点变量与零值比较:不可以将浮点变量用“==”或“!=”与任何数字比较。

千万留意,无论是float还是double类型的变量,都有精度限制,所以一定要避免将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。假设浮点变量的名字为x,应该将

if(x == 0.0)  //隐含错误的比较
6 F& d6 |" V3 I  s6 g% a5 u

转化为

if((x >= -EPSINON) && (x <= EPSINON))
" B& a: {& V' @. E//其中EPSINON是允许的误差(即精度)。

. U8 r+ H  \5 x

(4)指针变量与零值比较:应当将指针变量用“==”或“!=”与NULL比较。

指针变量的零值是“空”(记NULL)。尽管NULL的值与0相同,但二者的意义不同。假设指针变量名p,它与零值比较的标准if语句如下:

if(p == NULL)
  R- F- A3 X; i8 Pif(p != NULL)   //p与NULL显式比较,强调p是指针变量

9 Y4 m3 w- U" {' V" e

不要写成:

if(p == 0)- Z) `; F! i+ k: K7 @$ O! j
if(p != 0)    //容易让人误解p是整型变量

" \8 P9 H" h" l# N

或者

if(p)
2 b6 a; ]; `' I. D( C: V; Y4 Aif(!p)    //容易让人误解p是布尔变量

1 a% K, r+ J5 D

(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;

( j) `3 }- P8 H& ^

改写成

if(condition). s4 J1 _5 k& f* Z3 _- h
{4 u: W' k5 k7 v' i: n2 @
    return x;
2 X4 Q' g; E  L7 T% ~0 |}, m( L3 a; |# q8 e2 h. [
else
4 ?9 K8 X2 k% B# B+ C2 N{; s8 K* e. Q. J1 s
    return y;
2 L* V% e7 a- z}

3 k+ i! H' L4 i) z% E* m

或者改成更加简练的:

return(condition ?x:y);
* f' L' o# M! F; Y5 `/ b
  • 循环语句的效率


    # W1 j  I' c$ C) ^6 |( m& o

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

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

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

for(row = 0; row < 100; row++)
5 V3 i/ i" C2 P( t{) Z8 B. X. q% Y5 }! `8 |6 S% u
   for(col=0;col<5;col++)
! L( c3 o% ~3 S, ]   {
# ?  k' q* [' F      sum = sum +a[row][col];
+ V5 n- X) z) |   }# q3 L4 g' V! o! A: @$ k: r2 m
}

& c) ^8 }+ S% @: I2 T- x

示例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
}

/ N! K( Q7 Z! U- i. s

(2)如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。

示例c的程序比示例d多执行了 N-1 次逻辑判断。并且由于前者老要进行逻辑判断,打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。如果 N 非常大,最好采用示例 d的写法,可以提高效率。如果 N 非常小,两者效率差别并不明显,采用示例 c的写法比较好,因为程序更加简洁。

示例c:效率低但程序简洁

for(i = 0; i < N; i++)
' z. }8 ^$ ]; T) `( G{
7 v, s' o/ m4 c. _+ U/ I, pif(condition)
7 l+ L! U( w3 h3 v& O    DoSomthing();
0 k9 H4 S' u! t( Z* j0 A2 i' C9 Ielse
  k& V7 Z7 T+ d; m+ k    DoSomthing();
6 X; g4 h! a* s, c0 ~: {1 u( [}

* h+ a' O! x# m

示例d:效率高但程序不简洁

if (condition)
5 @" e+ R8 y/ o; J: F* z{
: z, S/ b3 T/ l) G    for (i=0; i<N; i++)
; A0 ^( i5 x3 Q0 z2 V+ J        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();
# _" t- g: {* u) q0 @}

! I/ T# B5 K. [: f

(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
{
$ q- d( Y% j- _: `" u/ `( ]}
  d- [& R% g7 Y2 b( U1 b

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

for(int x = 0;x <= N-1; x++)
) n( c+ M. J  e7 C* a- ~* ?. b1 Z{
; _8 G+ @* j0 G}

3 h' Z6 q: C7 v2 m. @
  • switch语句 switch是多分支选择语句,而if语句只有两个分支可供选择。虽然可以用嵌套的if语句来实现多分支选择,但那样的程序冗长难读。switch 语句基本格式:


    # {# u8 i) Y/ `
switch(variable)
$ q$ ^% G7 V9 I/ w{  _2 I, J* j8 k6 T+ o7 D6 V
    case value1:
# w1 ^" z+ f& J3 [                  break;
' p/ O, Y) }3 X; z) ~    case value2:9 f% U9 i0 p7 U8 E( ?
                  break;" v4 v: T( k2 g9 p
        default:. K( }, D" n7 N  f9 u
                  break;
5 W. h" u. c' a1 N. w/ o- X}
' X0 E" {! z6 i+ i

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

  • goto语句


    8 E& D  B4 \, f: f7 ~

自从提倡结构化设计以来,goto 就成了有争议的语句。

  • 由于 goto 语句可以灵活跳转,如果不加限制,它的确会破坏结构化设计风格。

  • goto 语句经常带来错误或隐患。它可能跳过了某些对象的构造、变量的初始化、重要的计算等语句,

    : a$ o" r6 c: s: g% h0 G6 ?$ l

例如:

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:

0 Y# Q+ f  F) q' J0 H6 a

如果编译器不能发觉此类错误,每用一次 goto 语句都可能留下隐患。很多人建议废除 C++/C 的 goto 语句,以绝后患。

但实事求是地说,错误是程序员自己造成的,不是 goto 的过错。goto 语句至少有一处可显神通,它能从多重循环体中一下子跳到外面,用不着写很多次的 break 语句; 例如:

{$ e8 K3 |% |; ]5 ^
{
. e* L( k# v. ~  ~4 ?! n3 ^! z6 Y   {; e$ Z' `/ Q* w0 J0 z
     goto error;" x, u/ n& D4 l7 j% f; x$ R  c6 ^+ _
   }
0 x/ o! I1 \+ l }. y& _$ r& T, p( b1 c1 ?
}! p: R: u- h, I# k& B0 t8 K8 ]1 E
error:

) |' \1 `, Q0 Z

就像楼房着火了,来不及从楼梯一级一级往下走,可从窗口跳出火坑,所以我们主张少用、慎用 goto 语句,而不是禁用。

小结

主要针对if、for、while、goto、switch等基本语句使用时可能出现隐患问题,归纳了正确使用它们的一些规则和建议。如有不对留言指正 参考资料:林锐《 c/c++编程指南》


% f- N* ]6 E0 P  e3 d
赞 收藏 评论1 发布时间:2020-6-9 19:23

举报

1个回答
李康1202 回答时间:2020-6-10 08:58:50
谢谢分享
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版