| 
 | 
	
    
 
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册  
 
x
 
if和for,这两个从本质上来说,是不是C语言的两个关键字呀。那么我们为什么又把它称之为C语言的基本结构的一部分呢。要回答这个问题,我们是不是首先要搞清楚C语言的基本结构是什么?/ ~) }; K+ S; s 
3 j. r9 T; r/ t. ]- E( t 
实际上,任何结构化编程语言的基本结构都是相同的,也就是三种基本的程序结构:顺序,分支和循环。由这三者最基本的结构,可以搭建出任何我们想实现的程序结构。在狄杰斯特拉(Edsger W. Dijkstra)反复研究面条式代码(spaghetti code),并在1968年给某位编辑写了一篇著名的简信,题为《Go to语句是有害的》之后,计算机科学家Corrado Bohm和Giuseppe Jacopini证明,使用顺序(sequencing),分支/选择(alternation)和循环(iteration)这三种流程结构就足以表达所有程序的本质。C语言作为结构化编程语言的一种,其程序结构,自然也是由这三种最基本的程序结构组成。7 e' B/ L2 q/ o 
) q0 X2 }" }) h: [& T" M' {- z# } 
顺序执行程序,这个很好理解,一条语句接着一条语句执行就可以了。那么C语言的分支和循环是如何实现的呢? 
& a$ U" r, Z  g) l0 m2 W8 a' f. w8 O3 I 
对于分支,我们常见的关键词是不是有if/else和switch/case两种组合呀。if/else翻译过来,是不是就是“如果…,否则…”,是一个条件判断。如果用伪代码的方式来进行表达的话,一般有这两种形式。. |( H0 T+ c) ^" _ 
) J3 b( u$ L3 T5 ^ 
第一种形式如下: 
+ W: X* P' n8 s2 @* Fif(条件为真)0 L) J+ f# m- t$ G 
{ 
) H" @) \: t; H/ r6 `代码段1; 
5 }# h. W# ?* O8 e( M$ R' _}' Z0 |; T" y, y, s1 J: _ 
else" x7 a! |6 c; j4 L" @  y  K 
{& ~3 }. Q, y% O3 z 
代码段2; 
4 A+ r& \( ~1 e9 ?2 D# }' {/ `; R} 
; C. l* b: J9 q5 R2 o3 W. O这里else的含义其实就是条件不为真,那么也就是条件为假。 4 I' b/ D1 `- \) h' c" S4 r 
2 P- F- G4 `* ~2 L' h% \' Z+ X% ]' t 
第二种形式是这样的:6 S: P9 x0 T3 ~7 d' `5 N 
if(条件1为真) 
- T0 f2 n2 }. G6 p9 L; O) @9 E{ 
0 W/ h2 ^* O( }6 F" ]代码段1; 
6 g2 _9 O9 b) s: M4 m} 
+ B% J$ X& z% relse if(条件2为真)$ W( S5 _# e- t% \: } 
{) W3 V; O3 Y: U5 \ 
代码段2;! C; k( [0 A9 }: V+ ^8 P0 C0 M 
}; Y  r! U) A$ F5 K& _4 H# X 
else5 F& U4 U0 m0 f. E 
{/ l6 t$ i9 v2 v8 j, ? 
代码段3; 
. L" Q3 ~% E9 A  s; l, j" z3 J. G' u 
}" ^% y0 n  i6 P# z; D9 A 
 
5 F# m: h" @6 c4 A9 }" F( j第一种形式和第二种形式本质上的区别,其实就是第一个是双分支,第二个是多分支。两种不同的分支,我们要根据具体情况去使用。某种意义上多分支模式可以由双分支演变而来,比如我们可以在双分支模式里面的else里嵌套一个双分支结构就可以了。5 T7 A( U, b/ i0 [) o5 ] 
 
6 j& D+ U( y5 o6 }' h2 L3 {; K" [& D 
1 M9 c* r7 R' X, Q8 M, \2 Y& \: M* Dif(条件1为真) 
" ]5 H3 R: `& U- X4 L{0 h' F* x, h( k3 l0 c5 C 
代码段1;5 ~6 h+ ^# r; o0 k' m4 ?, R  F 
} 
& p4 Q+ c; \3 Y! y& `else! o/ W5 F4 u: a5 J: t; l, ^3 P 
{ 
, a5 ^0 d  A( N/ j1 ]- |. eif(条件2为真)' g& N; C3 ]+ f, Y9 i 
{ 
  e* u* w2 ]( r+ _代码段2;  C+ A" z. m' m$ \# e6 l3 h, o 
} 
9 b4 T& ?7 p3 e; b7 xelse 
& }0 N1 A7 m7 H$ R2 \- w$ x; N7 ]) E{; ^& Z' [1 K9 U: Y 
代码段3; 
! E- f8 ]6 }6 s6 Q( g) l& O5 ^} 
  ]) z' u- W3 ?' k2 l0 a}, y* x# W  r; ^. W* ]7 K 
从这个意义上看,双分支和多分支其实是一回事情,本质上都是分支。分支这个概念,大家应该都是相对容易接受的,任何一件事情总有它的对立面,高对低,胖对瘦,大对小,物质对暗物质等等。分支这个概念应该是反映了事物的一种本源的状态,是描述程序是不可再进行切分的维度,也就是说分支成为了任何程序的三种基本结构之一。 
& X1 r" a+ U$ p& w& P+ j+ d+ u: y6 P3 X 
其实对于多分支的情况,C语言有另外一套关键字组合switch/case,写成伪代码的形式,大概是这样的。5 S1 @( U5 u. V0 n4 U0 b0 n8 U 
 
$ d9 N* Y7 R3 i, N% e" rswitch(变量){; W; M' }2 x4 k9 h% X9 P" i 
case 常数1: 
- X: s" B% S, S0 n+ T% {代码段1; 
! O/ M- J" X# R9 L% y2 Qbreak; 
6 n: ^9 O, A$ |$ Z( T- H4 pcase 常数2:- \' Y0 ^" f/ Y0 R3 U 
代码段2; 
  Z) k1 f/ I" }1 obreak; 
+ G4 W- K5 U/ ^! Dcase 常数3:. e9 E8 ~! R4 ~  H7 F2 A 
代码段3;- U/ d. q7 P# _" a1 F; { 
break;6 j9 u& Y  O: A; R3 [& R" q 
。。。' r" B0 `1 M7 t8 F% \6 L 
default:6 _4 M. ~. \8 l0 J% g1 \' J 
代码段n;' V5 d3 s! \& ]* k$ L& w 
break; 
/ M' Y" I; s; e2 f; o7 x$ \' X2 U} 
  C7 J0 f6 f* o& \* O" E 
' _! I9 w2 v& ~( R大家注意没,对于switch/case组合来说,它的条件一定要是常量,而且要是整数。这个是不是对判断的条件作出了限制呀。这里的default关键字,是和if/else里面的else对应的,表示意外情况。从表面看,switch/case适用于逻辑条件简单,但是分类较多的情况;if/else适用于判断条件复杂,但是分支较少的情况。但是从另外一个层面看来,switch/case所具备的功能,if/else完成起来,完全没有问题呀。那为什么还要搞出来这一个关键字组合呢? 
8 U, J7 u. Q" l- {' P4 f5 y# A0 z我个人的理解是switch/case关键字的执行效率,在某些情况下,要比if/else要高。3 C. Y! @( d+ h" R  @ 
: r& R9 y0 b$ g" Y# u# T/ i6 c: H, q 
int a = 0; 
5 f+ R" y' ^$ _switch(a): h6 C- K8 m, D8 L* L3 `% Q4 m 
{ 
; c" H4 z; X+ v+ Z- Lcase -1: 
9 Y4 o/ f0 N$ B: x/ H: R    break; 
' V5 |2 R, h& J# @' ~" @* ]; Acase -2: 
# @& |. A3 H4 ~9 o    break; 
( l, S1 |2 D( h! wcase -3:2 K4 W1 u. K& z, n2 ?! Y 
    break;3 w; K/ d! K* U 
case -4:4 }6 Q. f: ^0 W! ^( ^ 
    break; 
  ~* N' ?/ p7 Pcase 0: 
5 \5 U5 z+ y/ ~. P    printf("I am in switch case!\n");: _$ Q8 u1 C' K6 K# x8 P. T 
    break; 
, C5 M* P" T3 S: P) _! q2 n% \default: 
, P& s( j% c0 Q& ^+ b  break; 
4 a( o% z: f$ I* ]! \} 
' `+ u  P1 {! a4 Pprintf("I am between the switch case and if/else if/else!\n"); 
4 h8 B8 p9 V" y$ l6 P7 }if (-1 == a)' `# y3 H8 G0 d0 W. z  d: J 
{ 
; u" L0 `' l$ J# d$ J- s4 X}4 j: l8 v7 A$ P8 o& \: V# @1 ? 
else if (-2 == a) 
1 o+ K; [' O" ~5 E' k{* ~; n& h! I# {! A 
} 
+ Z6 K+ |; w/ L" Melse if (-3 == a) 
* Y2 b3 Q" [# z. P( ]" D{. ^! p% e' b  y4 F7 v! a 
}  A3 `+ Y( j9 w# u 
else if (-4 == a)1 g0 G' _1 W; V2 k' H 
{ 
& e& G7 z8 Y6 l+ l}4 D- |- a! Q0 s# A; v 
else if (-5 == a) 
) H8 f+ n* y2 \4 B) B8 ~{ 
* ~" m9 ^' d7 x1 o} 
$ k& j# m, u; S, a$ \. R% Felse* U& s5 r7 h& w 
{ 
8 O' c+ `% m- w9 d     printf("I am in if/else if/else!\n"); 
+ [1 ~9 G0 w- j4 M5 k) Y}' T; ]. R3 G/ X$ E0 z1 @% A' u* H 
 
; y+ Z; e3 S/ p, I9 z, a: p比如对于上面的代码段,对于switch/case关键词对来说,程序是直接跳到case为0的情况下的;但是对于if/else而言,程序则是一句一句比较之后才达到了“else”这一句,程序执行效率的高低是显而易见的。 
* s% N! K1 v. {5 A4 W 
# s, K; S% _0 |! q5 Q$ Z) O但是我们说,switch/case的程序执行效率可以比较高,并不是没有条件的。从汇编语言的层面来看,switch/case是建立了一张跳转表,因此需要一定的空间才行。这里某种程度上有以空间换时间的意思。 
  r, H! N: y3 B( o/ P( a4 z% a& u5 ~* A) O4 S; w4 `3 e+ \7 F 
因此,如果程序可以使用switch/case尽量使用这个,以便提高它的执行效率。其实,我们这样比对一番之后,自己也就轻而易举地牢牢记住了它们,这个可能也就是知其然知其所以然的效果,符合人的记忆规律。0 u# [, X: B9 A. I8 s( ` 
6 R7 i! w" k5 @, {. \/ I( P' U+ _ 
讲完了分支,我们来看一下循环。循环这个基本结构,在C语言里面,一共有两种实现方式,for循环和while循环,其中while循环还可以分为两种,一种是while循环,一种是do/while循环。我们下面分别看一下,这三种结构的程序表达大概是什么模样。 
7 |  c" K1 P( i: ?$ a/ Z# M 
. B4 z, \+ }, t# r' Rfor(循环控制变量初始化;循环终止条件;循环控制变量增量){循环体;} 
5 f! B5 J& ^* c) zfor循环的执行步骤是:首先执行循环变量的初始化,然后执行循环终止条件;如果判断条件为假(不符合终止条件),那么就开始执行循环体;然后执行循环控制变量的增量程序,执行完以后,再去判断是不是符合循环终止条件;如何符合条件,那么就退出循环;如果不符合条件,那么就继续执行循环体,并重复执行上述步骤。) I0 F4 Y' S& ?2 T' p 
 
5 Q  A% \( n2 o+ C$ u) ^( H感觉用第二种方式来描述这个循环体的执行过程,更为清晰。第一,先进行循环控制变量初始化;第二,执行循环终止条件,如果判断结果为假,则进入第三步;如果为假则循环终止并退出;第三,执行循环体;第四,执行循环控制变量增量,转入第二步; 
/ P0 b" F8 _- I% V' f1 w 
; t# c: x2 W6 I1 s0 U0 i注意,其实for循环括号中的三部分其实都可以省略,如果全部省略了,就变成了一个无限循环的死循环,跳不出来了。无限循环在操作系统中使用的是非常多的,每一个任务都是一个无限循环体,包括main函数也是一个无限循环体。 
2 K; U4 i" R0 L: M; l/ ? 
$ M$ _* E2 ~7 M0 A 
7 U( D& q, R" q$ E. t4 P& a& M) a7 Z) Q4 c* ?5 s 
0 T6 y- o* J: Z" z: A! O4 i4 W 
 |   
 
 
 
 |