C语言的结构
C语⾔是结构化的程序设计语⾔,这⾥的结构指的是顺序结构、选择结构、循环结构。我们平常所写的C语言便是顺序结构,即从前往后逐步执行语句。选择结构便是根据表达式的值来决定执行哪一个语句或块(复合语句),它的代表便是选择(或者称为分支)语句,即if和switch语句。循环结构便是根据表达式的值来决定是否重复执行某一语句或块,它的代表便是for、while和do……while语句。
本章集中于C语言的分支,通俗来讲,分支就相当于我们做选择,满足不同条件时,我们会选择做不同的事。由此C语言分支有两大势力,if和switch。
样例
在讲解选择结构之前,我们可以先看下面的代码:
#include <stdio.h>
int main(void)
{
int age;
printf("请输入你的年龄:\n");
scanf("%d", &age);
if (age >= 18)
printf("成年了\n");
return 0;
}
这是一个简单的程序,它要求你输入你的年龄,然后根据你的年龄便输出不同的结果。例如:
接下来我们来分析这串代码。
关系运算符和表达式
age >= 18是典型的关系表达式。
C语言中用于比较的表达式叫做“关系表达式”,其中的运算符便是“关系运算符”,关系运算符主要有以下六个:
关系运算符 | 含义 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
== | 等于 |
!= | 不等 |
关系运算符一般会对两侧操作数进行比较,而由此所组成的表达式的值便是真和假。例如:
关系表达式 | 相应的值 |
5 > 3 | 真 |
3 > 10 | 假 |
3 == 5 | 假 |
在C语言中,用0表示假,非零表示真,一般用数字1表示真。而关系表达式通常会在选择结构和循环结构中作为测试条件。
逻辑运算符
了解了关系运算符及表达式后,我们已经能编写简单的测试条件了,但当我们的测试条件是多个的时候,又将如何编写条件呢?
比如我们要表示数学中未知数x的取值范围为[4,6],我们平常都会写出不等式4 <= x <= 6来代表这个取值范围,那么编写代码时怎么做呢?
C语言提供了逻辑运算符,来构建更复杂的表达式,其主要有:
- !: 逻辑取反运算符
- &&: 逻辑与运算符
- ||: 逻辑或运算符
&&
逻辑与运算符表示并且的意思,只有当两侧的表达式皆为真时,整个表达式都为真,否则为假。例如:
6 > 3 && 3 == 3 真
6 < 3 && 3 == 3 假
6 < 3 && 3 == 0 假
||
逻辑或运算符的意思为或者,当两侧表达式只要有一个为真,整个表达式都为真。例如:
6 > 3 || 3 == 3 真
6 < 3 || 3 == 3 真
6 < 3 || 3 == 0 假
!
逻辑取反运算符(或者是逻辑非运算符)则是按表达式的值取反。例如:
!(6 == 3) 真
!(6 < 3) 真
!(6 > 3 && 3 == 3) 假
所以,当我们想要表示4 <= x <= 6时, 要写成x >= 4 && x <= 6。那么在程序中 4 <= x <= 6是什么情况呢?
优先级和求值顺序
关系运算符的优先级比算数运算符低,比赋值运算符高,所以x > y + 5 和 x > (y + 5)是等效的。
关系运算符的求值顺序是从左到右,即x > 5 > y 和 (x > 5) > y等效。
所以4 <= x <= 6是有效的,但它并不表示x的取值范围为[4,6]。
4 <= x <= 6可以写成(4 <= x) <= 6。当x大于4后,前一个表达式值为1(一般情况),1小于6,表达式值为真;当x小于4,4 <= x值为0,小于6,表达式值为真。所以,无论x值为多少,4 <= x <= 6的值总为真。
在逻辑运算符中,!的优先级最高,其次是&&,最后是||。所以,!(6>3) || x >= 4 && x <= 4 和 !(6>3) || (x >= 4 && x <= 4)是等效的。
在C语言中,有逻辑运算符参与的表达式的求值顺序是从左到右的。在或运算和与运算中,会先对左边的表达式求值,后对右边的表达式求值。
明白了求值顺序后,逻辑运算符也有短路的情况(一般和&&和||有关)。即当左侧表达式的值能够决定整个表达式的值后,便不对右侧表达式求值。对于&&来说,当左侧表达式值为假(0)时,此刻无论右侧表达式值为多少,整个表达式的值为假(0);对于||来说,当左侧表达式值为真(非零)时,此刻无论右侧表达式值为多少,整个表达式的值为真(非零)。在这两种情况中,程序便不会对右侧表达式求值,这便是逻辑运算符的短路。
语句
在正式开始前,我们先了解一下语句。
语句是C程序的基本构建块。一条语句相当于一条完整的计算机指令。在C中,大部分语句都以分号结尾。因此,
a = 4
只是一个表达式,而
a = 4;
是一条语句。
C语言中最简单的语句便是空语句:
; \\空语句
它在C语言程序中不会有任何作用,唯有在需要满足语法需求但是又不需要发挥任何作用时可以考虑空语句。
C语言的语句可以分为简单语句和复合语句。简单语句以一个分号结尾。如下所示:
赋值表达式语句: a = 12;
函数表达式语句: printf("%d\n", s);
空语句: ; //什么都不做
复合语句(或者被称为块)由花括号括起来的一条或多条语句组成。复合语句在语法上被当作一个语句,例如:
if (age >= 18)
{
printf("成年了\n");
printf("成年了吗?\n");
}
虽然块中有两个语句,但整个块被当作一个(复合)语句,所以满足if后执行语句只能为一个语句的条件。
if语句
if (age >= 18)
printf("成年了\n");
这是一个典型的if语句,当age大于等于18时输出,小于18时什么都不输出。if语句在选择结构中是最简单的语句,它根据表达式的值来决定是否执行下一条语句,值为真则执行,反之,跳过。
它的基本形式是 :
if (表达式)
语句;
注意:if语句中的执行语句只能为一条(或者说if只能控制一条语句),当待执行的语句有多条时,需要使用复合语句,例:
#include <stdio.h>
int main(void)
{
int age;
printf("请输入你的年龄:\n");
scanf("%d", &age);
if (age >= 18)
printf("成年了\n");
printf("成年了吗?\n");
return 0;
}
运行结果:
如上图所示,无论是否满足条件,都会输出“成年了吗?”,而 “成年了”则会根据条件选择输出。如果想同时根据条件输出这两句话,一个方法是更改printf函数的格式化字符串,即:
printf("成年了\n成年了吗?\n");
另一个方法便是用{}将这两条语句括起来,形成一个复合语句从而令if控制这两句,即:
{
printf("成年了\n");
printf("成年了吗?\n");}
if...else...语句
if语句是根据条件决定是否执行下一语句,但在解决问题的过程中,仅仅有if是不够的,我们遇到的情况更多是根据条件的不同执行不同语句,这时我们便可以使用if...else...语句,和判断对错本质相同。接下来看一个程序:
#include <stdio.h>
int main(void)
{
int score;
scanf("%d", &score);
if (score >= 60)
printf("合格\n");
else
printf("不合格\n");
return 0;
}
这个程序非常简单,便是根据输入的成绩是否大于60而做出不同的反应。由此看来,if..else..语句的典型形式是:
if (表达式)
语句
else
语句
当表达式为真时,执行if语句,否则执行else语句。同样的,这里的语句可以是一条简单语句,也可以是复合语句。
else if多重条件
尽管if...else...可以表示选择,但它能表示的仅仅是两个对立条件,即不是往东就是往西。但条件不仅仅是对立的,也存在多个条件多个选择的情况。就像老师对学生的成绩进行分级,优、良、及格、不及格这四个等级对应着不同的成绩范围。else if的典型形式是:
if (表达式)
语句
else if (表达式)
语句
……
else
语句
从if开始判断,当if测试条件为假时,判断下一个表达式的真假,如果存在真,便执行相应语句,之后不再进行判断;当表达式均为假时,则执行else语句。这里的语句与上一个语句相同。在这里献上一道题:
题目描述:
编写程序,根据某城市普通出租车收费标准进行车费计算。标准如下:
起步里程为3公里,起步费10元;
超出起步里程后10公里内,每公理2元;
超过10公里以上的部分加收50%的回空补贴费,即每公里3元;
营运过程中,因路阻及乘客要求临时停车等待的,按每5分钟2元计费(不足5分钟不收费)。输入格式:共一行,包括行驶里程(单位为公里,精确到小数点后1位)与等待时间(整数,单位为分钟),其间以空格分隔
输出格式:在一行中输出乘客应支付的车费(单位为元),结果四舍五入保留整数。
示例:
输入:40.0 7
输出: 116
if嵌套
我们看下面的程序:
#include <stdio.h>
int main(void)
{
int score;
scanf("%d", &score);
if (score >= 85)
printf("优秀\n");
else
{
if (score >= 70)
printf("良好\n");
else if (score >= 60)
printf("及格\n");
else
printf("不及格\n");
}
return 0;
}
这是典型的if嵌套,if嵌套的意思为当根据测试条件执行语句时,执行的语句同样可以是条件语句(if、if...else...、else if)。
else配对
当我们进行if嵌套时,就会遇到else的配对问题 。当有多个if和else时,else会与和它最近的if进行配对。例如:
#include <stdio.h>
int main(void)
{
int a = 0, b = 0;
if (a == 1)
if (b == 0)
printf("haha\n");
else
printf("hehe\n");
return 0;
}
在缩进上,else与第一个if进行配对,但实际上,else与第二个if进行配对。这个程序运行结果便是什么都不打印。
如果想让else与第一个if进行配对,则需要使用{},即:
switch语句
使用条件运算符和if else 可以编写二选一的程序,当涉及到多重选择时,我们可以选择if……else if……else来实现,但有时使用switch语句更方便。下面代码演示了如何使用switch。
#include <stdio.h>
int main(void)
{
int month;
scanf("%d", &month);
switch(month)
{
case 1:
printf("January");
break;
case 2:
printf("February");
break;
case 3:
printf("March");
break;
default:
printf("I am lazy to print\n");
break;
}
return 0;
}
该程序读入一个数字,输出相应月份的英文(仅限1-3月)。
对于switch语句,程序首先对switch后圆括号的表达式求值。在上述代码中便是month的值。然后程序扫描标签(这里指,case 1:、case 2:等)列表,知道发现一个匹配的值为止,然后程序跳转到那一行。如果没有匹配的标签,如果有default :(标签行),就跳转至该行;否则,便退出switch语句,执行之后的语句。
注意:switch在圆括号的测量表达式的值应该是一个整数值(包括char类型)。case标签必须是整数类型(包括char类型)的常量或整型常量表达式。不能用变量作为case标签。同时,C语言中一般case都指定一个值,不能使用一个范围。
switch语句构造如下:
switch (整型表达式)
{
case 常量1:
语句
case 常量2:
语句
default:(可选)
语句
}
注意:
- switch语句中,每一个标签后的语句都不需要使用大括号;
- 同时,default在switch中可以省略;
- 如果switch中存在default,default的位置没有要求。即它可以在开始、结尾或者中间。无论其位置在何处,只有所有case标签和表达式的值不匹配时,才执行标有default的语句。
break在switch中的作用便是跳出其他标签语句。在上述代码中,break便是在输出相应月份的英语后,跳过其他语句。如果没有break语句,程序便会从匹配行开始,依次执行之后的代码。例如:
多重标签
知道break的效果后,我们可以在switch中使用多重标签。请看下面代码,这个代码作用是根据输入的月份输出相应的天数。
#include <stdio.h>
int main(void)
{
int month;
printf("请输入月份(数字):");
scanf("%d", &month);
switch (month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
printf("31\n");
break;
case 4:
case 6:
case 9:
case 11:
printf("30\n");
break;
case 2:
printf("28 or 29\n");
break;
}
return 0;
}
if else 和switch
关于何时使用这两个语句,如果是根据浮点类型的变量或表达式进行选择,就无法使用switch。如果根据变量在某个范围进行选择,使用if else更为方便。
参考《C Prime Plus》。