|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
if和for,这两个从本质上来说,是不是C语言的两个关键字呀。那么我们为什么又把它称之为C语言的基本结构的一部分呢。要回答这个问题,我们是不是首先要搞清楚C语言的基本结构是什么?
: z0 Y5 P- z1 U4 s$ t$ Q7 O
% n' j/ p7 @4 P3 ?) A7 H. d" M实际上,任何结构化编程语言的基本结构都是相同的,也就是三种基本的程序结构:顺序,分支和循环。由这三者最基本的结构,可以搭建出任何我们想实现的程序结构。在狄杰斯特拉(Edsger W. Dijkstra)反复研究面条式代码(spaghetti code),并在1968年给某位编辑写了一篇著名的简信,题为《Go to语句是有害的》之后,计算机科学家Corrado Bohm和Giuseppe Jacopini证明,使用顺序(sequencing),分支/选择(alternation)和循环(iteration)这三种流程结构就足以表达所有程序的本质。C语言作为结构化编程语言的一种,其程序结构,自然也是由这三种最基本的程序结构组成。% R9 u! K+ d3 T! W
& A; S6 |) O/ }( G, k; w顺序执行程序,这个很好理解,一条语句接着一条语句执行就可以了。那么C语言的分支和循环是如何实现的呢?
* A3 Q8 I) W% u( t" \, F4 P ~; L" K
对于分支,我们常见的关键词是不是有if/else和switch/case两种组合呀。if/else翻译过来,是不是就是“如果…,否则…”,是一个条件判断。如果用伪代码的方式来进行表达的话,一般有这两种形式。
' {* r/ H$ Q9 y* j0 A- G9 C8 p: H- u% Y7 l P/ ?5 ^ a
第一种形式如下:
0 O- R" g& p6 w: u4 j! `- Kif(条件为真)
1 g$ A# B1 T- O0 q0 u6 e K3 V{$ L# _5 k. U! w; @- _: U$ |
代码段1;# c0 @( }- `" H! R
}
) u, A4 U8 {1 s1 h Eelse' M6 M N# Y2 h. e
{ {+ n2 |: b% f
代码段2; f: ]3 v' n5 t: z6 ^, ^
}
( A! u6 h# _" c4 U. ^这里else的含义其实就是条件不为真,那么也就是条件为假。 ' c; z; l# ?5 U7 M. z c$ T+ A
# _" ~+ }. M, w, Y" @# H, y# A% y第二种形式是这样的: l8 K2 I. O( n- W2 _7 t! @ R
if(条件1为真)
; ~2 H. u7 [6 ?3 F2 H{+ z# j% _. O* y! |0 ]+ W' o0 r
代码段1;% q4 E2 J" V1 p% w% V# k ^
}
8 v W; k+ {7 D* }1 r$ Lelse if(条件2为真)/ n8 M& l4 q3 a/ _9 i, [9 y
{
. w2 b8 U: ]" Y% i$ G0 {8 {/ c' K代码段2;8 @7 i3 _7 ~* { w5 k. J
}
: N: s9 A W" pelse
, [( `7 j6 i2 R- D# m0 }# F2 [& E{
! U& ^ O6 F% d$ T1 Z' p代码段3;$ j5 l8 u! e/ m; n
m* y n% P' }+ C7 P0 R
}3 y0 d7 A9 o/ t2 E
8 A, e3 [/ Q# c+ Z- X. u! _# ^
第一种形式和第二种形式本质上的区别,其实就是第一个是双分支,第二个是多分支。两种不同的分支,我们要根据具体情况去使用。某种意义上多分支模式可以由双分支演变而来,比如我们可以在双分支模式里面的else里嵌套一个双分支结构就可以了。
5 x3 H( d: \, {" m X: S
: E9 B- E0 y+ O, I' v# z9 D D3 n
if(条件1为真); F4 v; ?! M4 J( l& @; y
{
4 V- I$ y0 _ p( ]代码段1;
. p p; K: Z4 P' [7 i! D2 U}# D4 u' a0 @- O1 a0 U }
else
- y, e y( J% y8 v{2 \# \. c. Q) w+ d
if(条件2为真)
. n6 z) _/ I; I" A+ n8 M S# P- f{
2 b% j9 {+ y3 ?代码段2;
' a$ B! g$ }6 n* X' T}* {3 D3 s# p. [! e/ x) E! m
else
% P% l: T9 M, h" r8 G, |/ L{8 B' y2 x" ?* {! _9 q% ]. X
代码段3;) ~6 H" h2 U/ G( J$ R' ]
}
: h2 ?6 M3 ~8 @1 E8 |; _}
6 v, m2 r9 e" S5 o* q) r从这个意义上看,双分支和多分支其实是一回事情,本质上都是分支。分支这个概念,大家应该都是相对容易接受的,任何一件事情总有它的对立面,高对低,胖对瘦,大对小,物质对暗物质等等。分支这个概念应该是反映了事物的一种本源的状态,是描述程序是不可再进行切分的维度,也就是说分支成为了任何程序的三种基本结构之一。
" ]9 M0 B+ l8 N
. ~4 M! [3 u6 K/ _& m其实对于多分支的情况,C语言有另外一套关键字组合switch/case,写成伪代码的形式,大概是这样的。
+ |1 T9 h. W6 L
# t) O% R6 X$ R0 Iswitch(变量){) R, N) k2 j1 B; G/ n$ O# R
case 常数1:0 |0 X" w( O6 h* {5 Z- R9 ^0 R
代码段1;7 O+ |" j% e* W: T
break;
; f. \+ G3 K/ {4 F) ?. A/ ucase 常数2:
4 `: A; C6 a" W* k8 A代码段2;% C: L& p/ X/ Q
break;
. h2 p/ X5 {) t/ |* h" `4 Dcase 常数3:
# W9 k K+ x' l1 k7 J代码段3;
6 H7 M/ d7 `0 Y) d. `9 n, j9 `. fbreak;7 }1 o; \! F5 w/ F% n
。。。, p/ O8 Y3 u. T' ~4 Z+ h2 T- A& z
default:
" J7 T e) T; i1 _4 o代码段n;
5 c. K% h1 U$ A0 U5 c7 w* Q1 W0 Fbreak;
. ~/ P% p. x0 `: }$ m}
3 o ], y! x# t. y" u
" [* A* p; N& L4 e, h# E! i大家注意没,对于switch/case组合来说,它的条件一定要是常量,而且要是整数。这个是不是对判断的条件作出了限制呀。这里的default关键字,是和if/else里面的else对应的,表示意外情况。从表面看,switch/case适用于逻辑条件简单,但是分类较多的情况;if/else适用于判断条件复杂,但是分支较少的情况。但是从另外一个层面看来,switch/case所具备的功能,if/else完成起来,完全没有问题呀。那为什么还要搞出来这一个关键字组合呢?
0 ^9 D( A$ \* n6 T我个人的理解是switch/case关键字的执行效率,在某些情况下,要比if/else要高。: g# G3 ?; }) G' t4 |
M. u. S4 J' X5 Q
int a = 0;2 q" c0 w! O! Q! v
switch(a)
9 ^0 Z. K, s5 n) G$ U{
$ Y0 Z- u1 t9 g: ?case -1:1 [: W, G, P/ q$ R: l
break;
4 h* M0 c0 P" }9 r5 w. ^case -2:
0 K6 q; m! l! A, G break;6 x" R" G8 r) V4 b
case -3:
/ r' x/ v: ]8 w" X, `+ O8 K break;% v3 A) B) G9 w) X
case -4:" \$ b# M: f- ^: E3 k
break;2 l: y1 q( Q& l" I! x$ m, W
case 0:
/ Z- w; b; U6 f+ i' }; m3 j6 r printf("I am in switch case!\n");
$ I% c: |, W7 @( H5 P: X break;( @/ d9 V' w8 S: [* _
default:
/ b. e2 S! T8 Q7 O) b, c break;7 j' T9 L* O8 n, S5 ^& I4 x* h
}
4 q/ m) |$ f2 {& Z( `0 t$ h6 aprintf("I am between the switch case and if/else if/else!\n");4 Z/ L0 m+ ^5 R9 r" ?
if (-1 == a)$ _5 M) O* J% ^
{
& A p+ \5 x1 R" S1 J+ `3 Q}( z2 s+ H/ M% T6 y' u& f
else if (-2 == a)' {9 ]6 u |, ?/ g M* X0 A
{. ~$ Q: w/ ~1 K7 M. v7 e
}7 T0 h; q; S8 d
else if (-3 == a); l' ^; j" }0 F6 O. `* M7 z
{. L; [4 p: w% h9 D) {& Z
}/ P. R$ ]5 q# V
else if (-4 == a)" K( ]. r' Q4 D7 _8 v
{7 U$ T4 W$ t- L( u ]
}, u- a9 E5 T1 X Z. s
else if (-5 == a)( l6 j* k6 u' \5 X- |
{
X1 [' G6 p3 f) W" P% E2 t; t}# u' _! E# S8 ^
else( @# f$ t, p- _
{
: B: b4 ?( S! w! V. ?. g x( D( o printf("I am in if/else if/else!\n");7 x$ x7 }5 P# m3 A# v1 g
}
# O: C' | N8 T2 ^$ g
0 E3 i% G# A0 y8 p5 M比如对于上面的代码段,对于switch/case关键词对来说,程序是直接跳到case为0的情况下的;但是对于if/else而言,程序则是一句一句比较之后才达到了“else”这一句,程序执行效率的高低是显而易见的。: `% r: c0 _- H. ?/ t8 {/ `9 w
. [: I+ {7 ?# o; Q但是我们说,switch/case的程序执行效率可以比较高,并不是没有条件的。从汇编语言的层面来看,switch/case是建立了一张跳转表,因此需要一定的空间才行。这里某种程度上有以空间换时间的意思。$ [. S" E7 g% T# D6 P
" Z9 B" Q* S7 A$ S% @$ c* s
因此,如果程序可以使用switch/case尽量使用这个,以便提高它的执行效率。其实,我们这样比对一番之后,自己也就轻而易举地牢牢记住了它们,这个可能也就是知其然知其所以然的效果,符合人的记忆规律。. J* H6 W$ j2 g& i8 a4 Z
$ F4 F2 l) M4 ^: C讲完了分支,我们来看一下循环。循环这个基本结构,在C语言里面,一共有两种实现方式,for循环和while循环,其中while循环还可以分为两种,一种是while循环,一种是do/while循环。我们下面分别看一下,这三种结构的程序表达大概是什么模样。
( u4 d5 c; R; q7 V( e, w/ E. ?9 z( P: I# {( r
for(循环控制变量初始化;循环终止条件;循环控制变量增量){循环体;}
" {! f; e; @( m! S9 o. v" \for循环的执行步骤是:首先执行循环变量的初始化,然后执行循环终止条件;如果判断条件为假(不符合终止条件),那么就开始执行循环体;然后执行循环控制变量的增量程序,执行完以后,再去判断是不是符合循环终止条件;如何符合条件,那么就退出循环;如果不符合条件,那么就继续执行循环体,并重复执行上述步骤。1 O1 E7 j `& T5 e, K7 C- A N: q0 z
9 v/ t) [2 ^/ A; |
感觉用第二种方式来描述这个循环体的执行过程,更为清晰。第一,先进行循环控制变量初始化;第二,执行循环终止条件,如果判断结果为假,则进入第三步;如果为假则循环终止并退出;第三,执行循环体;第四,执行循环控制变量增量,转入第二步;
" W/ q: d! L0 r$ O2 A; i, Z% K7 Q7 T$ J; d4 q
注意,其实for循环括号中的三部分其实都可以省略,如果全部省略了,就变成了一个无限循环的死循环,跳不出来了。无限循环在操作系统中使用的是非常多的,每一个任务都是一个无限循环体,包括main函数也是一个无限循环体。
- X- F; z9 f, G. Z9 z" t8 L i3 B0 z- u3 w- ^" ]& w. G
- Q0 u( W( e+ L; o1 a" C
0 \; W7 C4 `2 b; P9 x9 w4 h4 F/ \( K- M* \6 U
|
|