|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
if和for,这两个从本质上来说,是不是C语言的两个关键字呀。那么我们为什么又把它称之为C语言的基本结构的一部分呢。要回答这个问题,我们是不是首先要搞清楚C语言的基本结构是什么?$ t$ S' w! E$ a
. h7 @8 e& b2 ?, B- M
实际上,任何结构化编程语言的基本结构都是相同的,也就是三种基本的程序结构:顺序,分支和循环。由这三者最基本的结构,可以搭建出任何我们想实现的程序结构。在狄杰斯特拉(Edsger W. Dijkstra)反复研究面条式代码(spaghetti code),并在1968年给某位编辑写了一篇著名的简信,题为《Go to语句是有害的》之后,计算机科学家Corrado Bohm和Giuseppe Jacopini证明,使用顺序(sequencing),分支/选择(alternation)和循环(iteration)这三种流程结构就足以表达所有程序的本质。C语言作为结构化编程语言的一种,其程序结构,自然也是由这三种最基本的程序结构组成。
9 t) o6 T6 J" a$ h% v1 W- H1 Z, J. }+ G
顺序执行程序,这个很好理解,一条语句接着一条语句执行就可以了。那么C语言的分支和循环是如何实现的呢?
! O. o& F9 ?3 `6 k8 }
! a7 f9 b1 _0 g: E4 i, |对于分支,我们常见的关键词是不是有if/else和switch/case两种组合呀。if/else翻译过来,是不是就是“如果…,否则…”,是一个条件判断。如果用伪代码的方式来进行表达的话,一般有这两种形式。
3 l! l( `9 G# c8 |7 ^* G% V- ]% ?
第一种形式如下:
. C2 i$ ~- [- ^9 r, _# x! Xif(条件为真)
6 c8 X9 }: T, j# j, @" N% p; F{
/ d2 `( @: X! q+ v1 Y. x3 i代码段1;# t' v Y; @. Q" I0 R: R
}
1 {; X/ Z9 j* X! T0 W3 Gelse2 ?* F* P3 U0 x/ u% q0 P
{$ M/ n% Y2 o0 I6 k$ R
代码段2;2 v+ D+ c9 c5 u1 p, U
}
! b! Y/ f6 r" ]: g, c这里else的含义其实就是条件不为真,那么也就是条件为假。 " O' h: ]6 S, \0 P- `8 h4 i
8 E" J8 ?$ e7 b' P) _
第二种形式是这样的:! z0 Y$ i6 A9 ]7 w( V* t
if(条件1为真)4 V( }' k! n, _$ k5 k, k% m
{8 M2 O) d- h7 u# `( {: N
代码段1;
/ @/ N- c* C1 ^$ L}, i, `: F4 M$ w$ M5 L* A6 ]
else if(条件2为真)- F2 i& R6 A. V( H1 x# Y
{
+ a8 ~5 J0 c; A. B% E代码段2;2 b6 E% {" Q. O( s; }/ d, @
}+ t% O* { q* w) J
else
+ A w2 Z) u2 I/ h{
3 B: a) {5 a# {& m代码段3;7 {2 ?; D6 g' s( |8 d, L3 q' g
% n4 A' c n8 Y d& j
}) D; _( o* E& P( U/ k
5 s: ~9 ~- e) g) L5 X4 w8 n第一种形式和第二种形式本质上的区别,其实就是第一个是双分支,第二个是多分支。两种不同的分支,我们要根据具体情况去使用。某种意义上多分支模式可以由双分支演变而来,比如我们可以在双分支模式里面的else里嵌套一个双分支结构就可以了。
; B# W$ b* s: x$ ^( G3 f9 u( ?8 I( T( j1 x8 w
# E& _0 M/ |. |! D& `0 @4 u
if(条件1为真)
- V% c4 ~# m! ~' j{% `8 i/ B" r: ]' a
代码段1;
) q0 i% E) u" j( [) Q; D8 r$ A}; v% A6 [2 ?4 A A; u; N) y
else, c2 i+ E# W }
{0 ~! v1 `; K; V2 H. |+ R) b
if(条件2为真)
7 {# q! y8 X5 Q) P2 ?{; y2 [( \* L. I3 e: {
代码段2;
; x+ u0 O! ?. B}% _& A% u6 [& I' O5 v
else4 n- J, p& v) m* P' c" m# D, F
{
$ W6 C- o5 Y8 T' I* w代码段3;6 s8 n/ x# ~3 ~7 z: l* P9 f* J
}1 ]! ^0 b' P( R3 R5 ~9 Z |" N
}
) `- k8 H( u* H6 p- K从这个意义上看,双分支和多分支其实是一回事情,本质上都是分支。分支这个概念,大家应该都是相对容易接受的,任何一件事情总有它的对立面,高对低,胖对瘦,大对小,物质对暗物质等等。分支这个概念应该是反映了事物的一种本源的状态,是描述程序是不可再进行切分的维度,也就是说分支成为了任何程序的三种基本结构之一。% {1 e' `& t7 h' g
6 Z/ u( B( D# X$ O& `, a$ b. X* s& U其实对于多分支的情况,C语言有另外一套关键字组合switch/case,写成伪代码的形式,大概是这样的。
5 V8 W- h8 N# O
+ S$ d; s' D$ v" `: M7 q8 p+ `switch(变量){
& [# I, c6 \$ D! u1 Zcase 常数1:
# U' p7 U# ^( P. \' [ T代码段1;
* I3 @6 n0 J/ K0 jbreak;( e: `6 M$ B* z- p. z% T
case 常数2:1 H# Y, f& B" O% u l. J" M
代码段2;4 Y1 d: K0 l. l: Y, I+ }
break;$ ~' X' S4 f" H% Z0 r2 a
case 常数3:
9 O7 c$ u: E" q0 i+ @/ ^6 K, C代码段3;
/ U( X( @) K" Wbreak;
; g* J0 m& @' _% ^* X。。。. N9 y- B+ _- t, X1 }
default:
, `5 y0 r, i) m0 Z9 o代码段n;" I# r/ h/ d4 F/ _
break;9 D$ L3 V0 r5 x b
}
2 m: ~' p+ k7 [3 \* ?) U. W* K/ H- T+ ~9 Q0 _" b
大家注意没,对于switch/case组合来说,它的条件一定要是常量,而且要是整数。这个是不是对判断的条件作出了限制呀。这里的default关键字,是和if/else里面的else对应的,表示意外情况。从表面看,switch/case适用于逻辑条件简单,但是分类较多的情况;if/else适用于判断条件复杂,但是分支较少的情况。但是从另外一个层面看来,switch/case所具备的功能,if/else完成起来,完全没有问题呀。那为什么还要搞出来这一个关键字组合呢?0 |! B8 `1 q! C3 t
我个人的理解是switch/case关键字的执行效率,在某些情况下,要比if/else要高。, m' _( U$ Y! r! Z
8 L* r$ t. s+ a. n5 @/ y$ Lint a = 0;* `3 z6 d4 K0 y$ m6 l. I
switch(a)$ j" K f; F1 M9 l' w/ _+ |; ]$ k
{% Q9 ^* c0 q0 z- T
case -1:0 e7 \2 Q( j* O5 `- h
break;
+ W9 v Z+ H$ N4 {# H# ]& s8 D% Dcase -2:+ G* V6 p+ d( |7 e
break;# m" Y; R8 x% `; \* G7 T8 S
case -3:
3 Y' @/ J- l; z, E6 y break;
, Z& I$ ?9 o( m0 Z- P1 `case -4:
( L' J0 y" Q! x$ p+ ]5 n2 Y; f break;9 ^1 U$ M0 H6 o
case 0:
( p1 a& c. [3 W v6 v printf("I am in switch case!\n");
* }7 Y. K# q1 i2 ~% W% q break;8 A6 }" \5 G" }: Y& b+ S
default:
u3 L9 a7 j, s! G* S" a( A$ J break;
8 _# U% [& y3 _3 ^% ~}1 S! v/ S* W' ]7 f6 d3 `
printf("I am between the switch case and if/else if/else!\n");- Y" `( g) _* M4 U9 }' J
if (-1 == a)
0 g8 U$ E: \2 K+ m" W+ T{. e q) Y @1 f. O
}" J6 K6 W9 B5 x* G! |2 F
else if (-2 == a)3 q2 L/ }6 [2 L
{& g- S/ X' Z0 k
}
1 P. O3 i- Z {! L0 A! N- T2 Eelse if (-3 == a)
- ?% z7 `5 t* q, h; o{. [, T, |& r% p( P1 b; T
}
# H) ]1 u8 l7 M: S, a( ^* N# s+ s* ?; delse if (-4 == a)
. u3 ~) Y& v$ k9 p{
6 I0 b1 P% C% q" B/ h}% i7 i5 E- w8 N, [1 p( w- b
else if (-5 == a)
+ B Z! }$ c' O7 K% l{
$ u; J, W7 x( ]- S3 [% ]+ i7 z1 @}! D, G3 R1 b. d& p3 _9 S, `
else
+ a% c/ H( r7 E, h/ @{
5 ^' n8 K# H7 k) e9 m printf("I am in if/else if/else!\n");
7 Y3 q! J- I: v) h+ N}
/ _% P, V* w& _2 C
& m1 M3 \" o% t比如对于上面的代码段,对于switch/case关键词对来说,程序是直接跳到case为0的情况下的;但是对于if/else而言,程序则是一句一句比较之后才达到了“else”这一句,程序执行效率的高低是显而易见的。* o5 W% N1 C D. p! Z
) w j4 w2 v1 B1 i( ?- s" h但是我们说,switch/case的程序执行效率可以比较高,并不是没有条件的。从汇编语言的层面来看,switch/case是建立了一张跳转表,因此需要一定的空间才行。这里某种程度上有以空间换时间的意思。
# | Y9 @' m0 M3 D( D' h) v, _/ h
+ P- d6 m. J+ H因此,如果程序可以使用switch/case尽量使用这个,以便提高它的执行效率。其实,我们这样比对一番之后,自己也就轻而易举地牢牢记住了它们,这个可能也就是知其然知其所以然的效果,符合人的记忆规律。
/ v" v& \. o, y7 p# Z5 S: `1 @$ c0 p3 M( R# p
讲完了分支,我们来看一下循环。循环这个基本结构,在C语言里面,一共有两种实现方式,for循环和while循环,其中while循环还可以分为两种,一种是while循环,一种是do/while循环。我们下面分别看一下,这三种结构的程序表达大概是什么模样。9 h9 X: H7 K( @
4 J7 j. B% i: |0 G3 l8 V/ @! H2 I
for(循环控制变量初始化;循环终止条件;循环控制变量增量){循环体;}; ?. }* O3 y$ z
for循环的执行步骤是:首先执行循环变量的初始化,然后执行循环终止条件;如果判断条件为假(不符合终止条件),那么就开始执行循环体;然后执行循环控制变量的增量程序,执行完以后,再去判断是不是符合循环终止条件;如何符合条件,那么就退出循环;如果不符合条件,那么就继续执行循环体,并重复执行上述步骤。- {' r/ P4 H) i
* I( A3 `# E# K) p2 A感觉用第二种方式来描述这个循环体的执行过程,更为清晰。第一,先进行循环控制变量初始化;第二,执行循环终止条件,如果判断结果为假,则进入第三步;如果为假则循环终止并退出;第三,执行循环体;第四,执行循环控制变量增量,转入第二步;
+ P- K4 [+ |% P G2 m! P& a: b/ s/ D) Y) m: r" m5 s1 ]0 c& i
注意,其实for循环括号中的三部分其实都可以省略,如果全部省略了,就变成了一个无限循环的死循环,跳不出来了。无限循环在操作系统中使用的是非常多的,每一个任务都是一个无限循环体,包括main函数也是一个无限循环体。 p- n% v8 V% R& Y4 U7 `
( z9 M. `! K! i
: E7 X/ v7 b' L- y& Q& {% J* g% c4 s! H& g0 E/ B0 |8 a
# J( h) F9 h; k. |- d |
|