前言
1.默认的运行流程
默认情况下,程序的运行流程是这样的:运行程序后,系统会按书写顺序执行程序中的每一行代码。比如下面的程序
1
#include <stdio.h>
2
3
int
main()
4
{
5
6
printf(
"
Hello-1\n
"
);
7
printf(
"
Hello-2\n
"
);
8
printf(
"
Hello-3\n
"
);
9
10
return
0
;
11
}
程序运行后,会按顺序执行第6、7、8行语句,于是输出结果为:
2.其他运行流程
但 很多时候,我们并不想要按照默认的运行流程去走,比如想在某个条件成立的情况下才执行某一段代码,否则不执行。比如微信的这个界面:
如果用户点击了注册按钮,我们就执行“跳转到注册界面”的代码;如果用户点击了登录按钮,我们就执行“跳转到登录界面”的代码。如果用户没做出任何操作,就不执行前面所说的两段代码。要想实现这种功能,那就要学会如何去控制程序的运行流程。
3.流程结构
为了方便我们控制程序的运行流程,C语言提供3种流程结构,不同的流程结构可以实现不同的运行流程。这3种流程结构分别是:
- 顺序结构:默认的流程结构。按照书写顺序执行每一条语句。
- 选择结构:对给定的条件进行判断,再根据判断结果来决定执行哪一段代码。
- 循环结构:在给定条件成立的情况下,反复执行某一段代码。
下面是这3种结构的流程图,大致预览一下即可
一、顺序结构
顺序结构是3种结构中最简单的,也是默认的流程结构:程序中的语句是按照书写顺序执行的。在 文章开头 开始列出的代码段,就是顺序结构,这里就不多介绍了。
二、选择结构1-if语句
C语言中选择结构的实现方式有两种:if语句和switch语句。先来看下if语句的使用,而if语句的形式是有好多种的。
1.形式1
先来看看if语句最简单的形式
1> 简介
1
if
( 条件 )
2
{
3
语句1;
4
语句2;
5
....
6
}
如果if右边小括号()中的条件成立,也就是为“真”时,就会执行第2~6行大括号{}中的语句;如果条件为假,就不执行大括号{}中的语句。这里的if是关键字。
2> 举例
1
int
a =
7
;
2
3
if
( a )
4
{
5
printf(
"
条件a成立\n
"
);
6
printf(
"
a的值为真
"
);
7
}
C语言规定所有非0值都为“真”,而a的值是7,因此第3行的条件是成立的,接着就会执行第5、6行代码。输出结果如下:
1
条件a成立
2
a的值为真
如果将a的值改为0,那么第3行的条件就不成立,就不会执行第5、6行代码
3> 省略大括号{}
如果if后面大括号{}中只有一行代码时,可以省略大括号。形式如下:
if
( 条件 )
语句1;
注意:如果条件成立,只会执行if后面的第1条语句;如果条件不成立,就不会执行if后面的第1条语句。
1
int
a =
7
;
2
3
if
( a >
9
)
4
printf(
"
aaa
"
);
5
printf(
"
bbb
"
);
因为第3行的a>9是不成立的,所以不会执行第4行代码。而第5行代码跟if语句是没有任何练习的,因此,第5行代码照常执行。于是会看到屏幕上只输出:
。
由于第5行代码跟if语句是没有任何联系的,所以一般会把代码写成下面这样:
1
int
a =
7
;
2
3
if
( a >
9
)
4
printf(
"
aaa
"
);
5
printf(
"
bbb
"
);
为了保证代码的可读性,不建议省略大括号!!!
4> 语句嵌套
if语句内部是可以嵌套其他if语句的,如下面的例子
1
int
a =
7
;
2
3
if
( a >
0
)
4
{
5
printf(
"
a的值大于0\n
"
);
6
7
if
( a<
9
)
8
{
9
printf(
"
a的值小于9
"
);
10
}
11
}
第3行的a>0是成立的,因此会按顺序执行第4~11大括号中的代码。执行到第7行的时候,a<9也是成立的,因此会执行第9行代码。输出结果:
1
a的值大于0
2
a的值小于9
5> 使用注意1
有些人习惯写完一行代码就在后面加个分号";",于是写if语句的时候,他们可能会这样写:
1
int
a =
6
;
2
if
( a>
8
);
3
{
4
printf(
"
a大于8
"
);
5
}
如果第2行尾部的分号,其实一个分号也是一条语句,这个叫做“空语句”。第2行的a>8不成立,所以不会执行后面的“空语句”。而后面的大括号{}跟if语句是没有联系的,因此会正常执行,于是会看到输出:
a大于8
所以要非常小心, 千万不要在if的小括号后面添加分号 。
第3~5行的内容是一个独立的“代码块”:
1
{
2
printf(
"
a大于8
"
);
3
}
6> 使用注意2
下面的写法从语法的角度看是对的:
int
a =
10
;
if
(a =
0
) {
printf(
"
条件成立
"
);
}
else
{
printf(
"
条件不成立
"
);
}
上述代码是完全合理的,编译器不会报错,只是个警告而已。因 为a为0,所以为"假", 输出结果是: " 条件不成立 "
这里隐藏着 很大的陷阱在:
假设你本来是想判断a是否为0,那么本应该写if (a == 0),若你误写成了if (a = 0),那将是一件非常可怕的事情,因为编译器又不报错,这样的BUG就难找了。因此, 像a==0这样的表达式,最好写成0==a,若你误写成0=a,编译器会直接报错的 。
//
不推荐
if
(a ==
0
) {
}
//
推荐
if
(
0
==
a) {
}
7> 使用注意3
在C语言中,可以不保存关系运算的结果。因此,下面的写法是合法的:
1
int
a =
10
;
2
a >
10
;
3
a ==
0
;
这里又是一个陷阱,假设你的本意是想给 a赋值为0,那么本应该写a = 0; ,若你误写成a == 0; ,那将又是一个非常难找的BUG,因为编译器根本不会报错。在1993年的时候,这个BUG差点让一桩价值2000万美元的硬件产品生意告吹,因为如果这个BUG不解决,这个产品就没办法正常使用
2.形式2
if还可以跟关键字else一起使用
1> 简介
1
if
( 条件 )
2
{
3
语句1;
4
}
5
else
6
{
7
语句2;
8
}
如果条件成立,就会执行if后面大括号{}中的语句;如果条件不成立,就会执行else后面大括号{}中的语句。总之,两个大括号中一定会有1个被执行,而且只能执行的1个。
为了减少代码行数,你也可以写成下面的格式:
1
if
( 条件 ) {
2
语句1;
3
}
else
{
4
语句2;
5
}
当然,也可以省略大括号,写成下面的格式:
1
if
( 条件 )
2
语句1;
3
else
4
语句2;
如果条件成立,就执行if后面的第1条语句;如果条件不成立,就执行else后面的第1条语句。但还是不建议省略大括号{}。
2> 举例
1
int
a =
10
;
2
if
( a==
0
) {
3
printf(
"
a等于0
"
);
4
}
else
{
5
printf(
"
a不等于0
"
);
6
}
第2行的a==0不成立,所以会执行第5行代码,输出结果:
a不等于0
3.形式3
if和else还有一种比较复杂的用法
1> 简介
1
if
( 条件1 )
2
{
3
语句1;
4
}
5
else
if
( 条件2 )
6
{
7
语句2;
8
}
9
else
if
( 条件3 )
10
{
11
语句3;
12
}
13
...
14
else
15
{
16
其他语句;
17
}
- 如果条件1成立,就执行条件1后面大括号{}中的内容:第2~4行
- 如果条件1不成立,条件2成立,就执行条件2后面大括号{}中的内容:第6~8行
- 如果条件1、条件2都不成立,条件3成立,就执行条件3后面大括号{}中的内容:第10~12行
- 第13行的...表示可以有无限个else if
- 如果所有的条件都不成立,就会执行else后面大括号{}中的内容:第15~17行
注意:这么多大括号中,只有1个大括号内的代码会被执行。跟之前一样,所有的大括号都可以省略,但是不建议省略。必要的时候,最后面的else那一段(第14~17行)是可以省略的。
2> 举例
1
int
a =
10
;
2
if
( a==
0
) {
3
printf(
"
a等于0
"
);
4
}
else
if
( a>
0
) {
5
printf(
"
a大于0
"
);
6
}
else
{
7
printf(
"
a小于0
"
);
8
}
第2行中的a==0不成立,接着会检查第4行。第4行的a>0成立,因此会执行第5行代码。输出结果:
a大于0
如果a的值是负数,那么第2、4行的条件都不成立,于是就会执行第7行代码。
三、选择结构2-switch语句
1.形式
先来看看switch语句的使用形式:
1
switch
(整型表达式)
2
{
3
case
数值1:
4
语句1;
5
break
;
6
case
数值2:
7
语句2;
8
break
;
9
... ...
10
case
数值n:
11
语句n;
12
break
;
13
default
:
14
语句n+
1
;
15
break
;
16
}
- 当整型表达式的值等于“数值1”时,就会执行“语句1”,后面的break表示退出整个switch语句,也就是直接跳到第16行代码;
- 当整形表达式的值等于“数值2”时,就会执行“语句2”;后面的以此类推。如果在数值1~数值n中,没有一个值等于整型表达式的值,那么就会执行default中的语句n+1。
- 由于所有的case后面都有个break,因此执行完任意一个case中的语句后,都会直接退出switch语句
2.举例
1
int
a =
10
;
2
3
switch
(a) {
4
case
0
:
5
printf(
"
这是一个0
"
);
6
break
;
7
case
5
:
8
printf(
"
这是一个5
"
);
9
break
;
10
case
10
:
11
printf(
"
这是一个10
"
);
12
break
;
13
default
:
14
printf(
"
什么也不是
"
);
15
break
;
16
}
因为a的值刚好等于第10行case后面的10,所以会执行第11行代码,输出结果:
这是一个10
3.break
break关键字的作用是退出整个switch语句。默认的格式中,每个case后面都有个break,因此执行完case中的语句后,就会退出switch语句。
1> 如果某个case后面没有break,意味着执行完这个case中的语句后,会按顺序执行后面所有case和default中的语句,直到遇到break为止
1
int
a =
0
;
2
3
switch
(a) {
4
case
0
:
5
printf(
"
这是一个0\n
"
);
6
case
5
:
7
printf(
"
这是一个5\n
"
);
8
case
10
:
9
printf(
"
这是一个10\n
"
);
10
break
;
11
default
:
12
printf(
"
什么也不是\n
"
);
13
break
;
14
}
- 由于变量a的值等于第4行case后面的0,因此肯定会执行第5行代码。
- 由于case 0中没有break语句,就不会退出switch语句,继续往下执行代码。
- 由于a的值已经等于第4行case的值,接着不会再判断a的值是否等于其他case的值了,直接按顺序执行第7、9行代码。在第10行有个break,接着就会退出switch语句。
- 输出结果为:
1
这是一个0
2
这是一个5
3
这是一个10
如果把a的值改为5,输出结果为:
1
这是一个5
2
这是一个10
2> 在某些时候,我们确实没有必要在每一个case后面添加break。下面举一个例子:判断分数的优良中差等级(100分满分)。
1
int
score =
77
;
2
3
switch
(score/
10
) {
4
case
10
:
5
case
9
:
6
printf(
"
优秀
"
);
7
break
;
8
9
case
8
:
10
printf(
"
良好
"
);
11
break
;
12
13
case
7
:
14
case
6
:
15
printf(
"
中等
"
);
16
break
;
17
18
default
:
19
printf(
"
差劲
"
);
20
break
;
21
}
- 当score的范围是90~100,score/10的值为10或9时,就会执行第6行代码,然后退出switch语句;
- 当score的范围是80~89,score/10的值为8时,就会执行第10行代码,然后退出switch语句;
- 当score的范围是60~79,score/10的值为7或6时,就会执行第15行代码,然后退出switch语句;
- 当score的范围并不是60~100,score/10的值并不在6~10范围内时,就会执行第19行代码,然后退出switch语句;
- score的值是77,所以score/10的值是7,输出结果: 中等
4.在case中定义变量
有时候,我们可能会想在case中定义一些变量,这个时候,就必须用大括号{}括住case中的所有语句。
1
int
a =
10
;
2
int
b =
4
;
3
4
char
op =
'
-
'
;
5
6
switch
(op)
7
{
8
case
'
+
'
:
9
{
10
int
sum = a +
b;
11
printf(
"
a+b=%d\n
"
, sum);
12
break
;
13
}
14
15
case
'
-
'
:
16
{
17
int
minus = a -
b;
18
printf(
"
a-b=%d\n
"
, minus);
19
break
;
20
}
21
22
default
:
23
printf(
"
不能识别的符号
"
);
24
break
;
25
}
在第10、17分别定义两个变量。输出结果:
a-b=
6
四、循环结构1-while循环
假如要你在屏幕上重复输出10次Hello World,你会怎么做?简单,把下面的代码拷贝10份就行了。
1
printf(
"
Hello World\n
"
);
没错,把上次代码写10遍,确实能实现功能。但是这样的代码太垃圾了,有很多的重复的代码,这样会使得代码非常地臃肿,复用率低。因此,不建议这么做。
下次遇到像上面那样重复执行某个操作时,首先要想到的应该是循环结构。所谓循环,就是重复执行某一个操作,C语言中有多种方式可以实现循环结构。先来看看while循环。
1.形式
1
while
( 条件 )
2
{
3
语句1;
4
语句2;
5
....
6
}
- 如果条件成立,就会执行循环体中的语句(“循环体”就是while后面大括号{}中的内容)。然后再次判断条件,重复上述过程,直到条件不成立就结束while循环
- while循环的特点: 如果while中的条件一开始就不成立,那么 循环体中的语句 永远不会被执行
可以省略大括号{},但是只会影响到while后面的第一条语句。不建议省略大括号。
1
while
( 条件 )
2
语句1;
2.举例
在屏幕上重复输出10次Hello World,每输出一次的换行。
1
int
count =
0
;
2
while
( count <
10
)
3
{
4
printf(
"
Hello World\n
"
);
5
6
count++
;
7
}
如果省略第6行的count++,count就一直是0,那么count<10一直都是成立的,这个while循环将会陷入“死循环”,一直在重复执行第4行代码。
3.注意
如果写成下面这样,也会让程序进入“死循环”
1
int
count =
0
;
2
3
while
( count <
10
);
4
{
5
printf(
"
Hello World\n
"
);
6
7
count++
;
8
}
- 注意第3行,while后面不小心加了个分号; ,一个分号表示一条空语句。
- 可以看出:while循环只会影响到第3行的空语句,而第4~8行的代码块是不受while循环影响的
- 由于count是0,那么count<10一直都是成立的,程序将会一直重复执行第3行的空语句,陷入死循环。
五、循环结构2-do while循环
形式如下:
1
do
{
2
语句1;
3
语句2;
4
...
5
}
while
(条件);
- 注意第5行,后面是加上一个分号;的
- 当执行到do-while循环时,首先会执行一遍循环体中的语句(“循环体”就是do后面大括号{}中的内容)。接着判断while中的条件,如果条件成立,就执行循环体中的语句。然后再次判断条件,重复上述过程,直到条件不成立就结束while循环
- do-while循环的特点: 不管while中的条件是否成立, 循环体中的语句 至少会被执行一遍
- 其实do while循环的用法跟while循环是差不多的,这里就不举例子了。
六、循环结构3-for循环
1.形式
for循环是所有循环结构中最复杂的。
1
for
(语句1
;
条件
;
语句2) {
2
语句3;
3
语句4;
4
...
5
}
- for循环开始时,会先执行语句1,而且 在整个循环过程中只执行一次语句1
- 接着判断条件,如果条件成立,就会执行循环体中的语句(“循环体”就是for后面大括号{}中的内容)
- 循环体执行完毕后,接下来会执行语句2,然后再次判断条件,重复上述过程,直到条件不成立就结束for循环
2.举例
1
for
(
int
i =
0
; i<
5
; i++
)
2
{
3
printf(
"
%d
"
, i);
4
}
输出结果为:
0
1
2
3
4
需要注意的是:变量i的作用域是第1~4行。一旦离开了这个for循环,变量i就失效了。
3.补充
如果for循环的初始化语句和循环一次后执行的语句是由多条语句组成的,就用逗号,隔开
1
for
(
int
x =
0
, y =
0
; x<
3
; x++, y+=
2
)
2
{
3
printf(
"
x=%d, y=%d \n
"
, x, y);
4
}
输出结果:
x=
0
, y=
0
x
=
1
, y=
2
x
=
2
, y=
4
七、break和continue
接下来,介绍两个比较重要的语句:break和continue。
1.break
前面在switch语句中已经用到了break,它的作用是跳出switch语句。它也可以用在循环结构中,这时候它的作用是跳出整个循环语句。
1> 举例
这里以for循环为例子,break也可以用在while循环、do-while循环中。
1
for
(
int
i =
0
; i<
5
; i++
) {
2
printf(
"
i=%d \n
"
, i);
3
4
if
(i>
2
) {
5
break
;
6
}
7
}
上面代码的意思是当i>2时,就跳出整个for循环,也就是结束for循环,所以输出结果是:
i=
0
i
=
1
i
=
2
i
=
3
2> for循环嵌套
先来看一个for循环嵌套的例子,嵌套的意思就是:for循环内部又一个for循环
1
for
(
int
x =
0
; x<
2
; x++
) {
2
for
(
int
y =
0
; y<
2
; y++
) {
3
printf(
"
x=%d, y=%d \n
"
, x, y);
4
}
5
}
输出结果是:
1
x=
0
, y=
0
2
x=
0
, y=
1
3
x=
1
, y=
0
4
x=
1
, y=
1
这个时候如果在for循环中加入一个break,那么这个break究竟是跳出里面还是外面的for循环呢?
1
for
(
int
x =
0
; x<
2
; x++
) {
2
for
(
int
y =
0
; y<
2
; y++
) {
3
printf(
"
x=%d, y=%d \n
"
, x, y);
4
5
break
;
6
}
7
}
注意第5行的break,这个break的作用是跳出里面的for循环,并非外面的for循环。所以输出结果是:
x=
0
, y=
0
x
=
1
, y=
0
如果改变一下break的位置
1
for
(
int
x =
0
; x<
2
; x++
) {
2
for
(
int
y =
0
; y<
2
; y++
) {
3
printf(
"
x=%d, y=%d \n
"
, x, y);
4
}
5
6
break
;
7
}
注意第6行的break,这个break的作用是跳出外面的for循环,并非里面的for循环。所以输出结果是:
x=
0
, y=
0
x
=
0
, y=
1
规律已经很明显了:break只会影响它所在的那个for循环
2.continue
continue只能使用在循环结构中,它的作用是跳过这一次循环,直接进入下一次循环。
这里以for循环为例子,continue也可以用在while循环、do-while循环中。
1
for
(
int
x =
0
; x<
10
; x++
) {
2
if
(x%
2
==
0
) {
3
continue
;
4
}
5
6
printf(
"
x=%d \n
"
, x);
7
}
注意第2行,当x%2==0,也就是当x是2的倍数时,就跳过这次循环,不执行第6行语句,直接进入下一次循环。输出结果:
1
x=
1
2
x=
3
3
x=
5
4
x=
7
5
x=
9
跟break一样,continue只会影响它所在的那个for循环

