作者:Mizuren Nanako, All Rights Reserved.
可以先看如下部分的代码:
#include <stdio.h> int main() { int a=0; scanf("%d", &a); if (a > 0) printf("positive\n"); else printf("negative\n"); return 0; }
很简单,就是读取int数a,根据条件a > 0
是否满足决定输出是"positive\n"
还是"negative\n"
。
注意到,一般程序是按顺序从上向下执行的,而这里使用if-else
结构产生了条件分支,使得程序有了选择和决策的过程。把这种对于程序每行指令的执行顺序有影响的结构叫作控制结构。
按照功能,可以把控制结构划分为几种类型:
goto
语句。严格的说,这不算是一种结构而是达成结构的手段。但是我也不建议在程序中使用goto
语句,这会导致程序可读性骤降和被老师骂。最为常见的是顺序结构。这种结构不需要特殊注明,因为程序中的所有语句都默认是顺序结构。如果一定要给出一个标记,我认为是大括号。所有被大括号包含的语句都默认遵循顺序结构。
C的分支结构实现上有if-else
和switch-case
两种,对应双分支和任意分支。
if-else
结构首先给出if-else
的完整结构:其中expression
、statement
均表示语句。
if (expression1) { statement1; } else if (expression2) { statement2; } else if ...... ...... else { statementN; }
直接理解:若expression1
为真,执行statement1
;否则,如果expression2
为真,执行statement2
;否则,……;否则,执行statementN
。
相信这个逻辑已经足够清晰。
switch-case
结构同样,给出基本形式:
switch(expression) { case value1: statement1; break; case value2: statement2; break; //...... case default: statementN; break; }
直接理解:先计算expression
的值。如果这个值是value1
,从case value1:
的标记处开始执行(重点!);如果这个值是value2
,从case value2:
的标记处开始执行;……;如果以上所有情况都不符合,从case default:
的标记处开始执行。
首先第一个重点就是这种结构是先计算了表达式expression
的值来进行比对的。第二个重点是当expression
的值等于某个标记例如case value1:
的时候,是从该标记处开始执行代码而不是只执行那个标记框定的代码。在这个例子里面,如果case value1:
的语句结束之后没有加多一句break;
,程序将会继续执行下去,执行case value2:
标记下面的代码……直到遇到break;
或者这一层的右大括号。请务必理解。 此外还要注意break;
的语义:无条件地离开当前所在的switch结构的大括号。(相当于直接跳到右括号)
为了更清晰,此处给出实例:
#include <stdio.h> int main() { int a=0; scanf("%d", &a); switch (a) { case 0: printf("0"); case 1: printf("1"); case 2: printf("2"); break; case 3: printf("3"); case default: printf("def"); } printf("\n"); return 0; }
此程序会要求一个整数a,接下来是获取到a的值之后程序的输出:
(为了和赋值号=
相区别,C用双等号==
作为等值判定号,此处维持C的习惯)
012
+换行符。12
+换行符。2
+换行符。3def
+换行符。def
+换行符。请对照理解。
循环结构是计算机能够进行反复的机械的操作的关键。
先配一张图:
这就是一个很典型的循环结构,expression1
执行完之后直接进入对expression2
的判断然后以此为循环条件进行循环。这张图相信这一届旧高考过的都会看。这个图片表达到c的代码里面是这样的:
for (expression1; expression2; expression3) { statement; } //离开循环体
请对照上图理解。
现在写一个循环的小程序:
(案例1)
#include <stdio.h> int main() { int s = 0, i = 0; for (i = 0; i < 100; ++i) { s += i; } printf("%d\n", s); }
输出应该是:5050
+换行符。
注意到expression1
其实可以写在for
外面,expression3
其实可以和statement
合并 (看流程图) ,所以这个程序又可以写成:
(案例2)
#include <stdio.h> int main() { int s = 0, i = 0; for (; i < 100;) { s += i; ++i; } printf("%d\n", s); }
这里发现好像expression2
是一定要写的,其实不是,也可以合并到statement
里面去。
(案例3)
#include <stdio.h> int main() { int s = 0, i = 0; for (;;) { if (i >= 100) { break; } s += i; ++i; } printf("%d\n", s); }
其中break
跳出当前层的循环。可以对比一下上面switch-case
的break
,这两者是一样的。
以上就是for
循环的所有形态。
对于案例2里面的for
的写法,只用到了expression2
,这种情况可以写成while
循环:
(案例4)
#include <stdio.h> int main() { int s = 0, i = 0; while (i < 100) { s += i; ++i; } printf("%d\n", s); }
有时候希望循环在每次循环结束的时候进行判断而不是在进入循环之前进行判断,那么有do-while
循环:
(案例5)
#include <stdio.h> int main() { int a = 0; do { printf("loop\n"); ++a; } while (a < 0); }
输出是:一行:loop
+换行符。
可以看到循环条件是否满足的判断是在每次循环的末尾进行的。
以上就是所有常规的循环结构。 此外,goto
语句也可以构建循环。见下文。
算法相关,以后单独开文章讲。
goto
语句为了防流氓题目,起码得会读。
和字面意思一样,goto XXX;
就是将程序执行点立刻跳转到标签XXX
处。看如下程序:
#include <stdio.h> int main() { int a=0; scanf("%d", &a); if (a > 0) goto hehehe; else goto hohoho; hahaha: return 0; hehehe: printf("positive\n"); hohoho: printf("negative\n"); goto hahaha; }
初看起来可能有点迷惑,其实十分清晰。
一开始程序顺序执行,到if (a > 0)
的时候进入条件分支。如果a > 0
成立,执行goto hehehe;
,进行一个到标签hehehe:
的跳转。跳转后下一跳语句是:printf("positive\n");
,按顺序往下走,是printf("negative\n");
,然后是goto hahaha;
,之后跳到标签hahaha:
,跳完下一条语句就是return 0;
,main
函数返回0
,因此程序结束。
回到刚刚的分支点:如果a > 0
不成立,执行goto hohoho;
,跳完下一条语句就是printf("negative\n");
,然后是goto hahaha;
,之后跳到标签hahaha:
,跳完下一条语句就是return 0;
,main
函数返回0
,因此程序结束。
goto
跳转的关键是要先插入标记,之后goto就可以无条件地跳到对应的标记。
另一个规则是:goto
只能在同一个函数内跳转。例如main
函数里面的goto
就只能在main
函数的大括号里面跳,不能跳到其他函数里面去。