选择语句
选择语句分为 if
语句和 switch
语句。
if语句
if
语句最简单的格式为:
if (expr) statment
比如下面这个示例
if (line_num == MAXLINES)
line_num = 0;
注意
不要混淆 ==
和 =
。语句 if (i == 0) ...
测试i是否等于0;而语句 if (i = 0) ...
则是先把0赋值给i,然后测试表达式的值是否非零,在这种情况下测试总是失败的。
程序实例
#include <stdio.h>
#include <stdlib.h>
int main() {
int a = 5;
int b = 2;
if (a > b) {
printf("%d\n", a);
}
return 0;
}
程序输出:
5
复合语句
如果我们想用 if
语句控制两条或者更多条语句,该怎么办呢?
这时就需要引入复合语句了。
复合语句格式如下:
{ statment }
通过将多条语句用花括号括起来,可以强制编译器将其作为一条语句来处理。如:
{
line_num = 0;
++page_num;
}
这样我们就可以在 if
语句中使用复合语句了:
if (line_num == MAXLINES) {
line_num = 0;
++page_num;
}
else子句
if
语句还可以有 else
子句,其格式为
if (expr) {
statment
} else {
statment
}
如果 expr
的值为0,那么就执行 else
子句。如:
if (i > j){
max = i;
} else {
max =j;
}
C/C++对 if
语句内部的语句没有任何限制。事实上,在 if
语句内部嵌套其他 if
语句是非常普遍的。比如,我们可以用下面的语句找出三个值中的最大值:
if (i > j) {
if (i > k){
max = i;
} else {
max = k;
}
} else {
if (j > k){
max = j;
} else {
max = k;
}
}
程序实例
#include <stdio.h>
#include <stdlib.h>
int main() {
int a = 1;
int b = 2;
if (a > b) {
printf("%d\n", a);
} else {
printf("%d\n", b);
}
return 0;
}
程序输出:
2
级联式if语句
写程序时,我们经常需要判定一系列条件,直到某个条件为真。级联式 if
语句往往是编写这类程序的最好方法。如:
if (n < 0)
printf("n is less than 0\n");
else
if (n == 0)
printf("n is equal to 0\n");
else
printf("n is greater than 0\n");
虽然第二个 if
语句是嵌套在第一个 if
语句内部的,但 C/C++ 程序员通常不会对第二个 if
语句进行缩进,而是写成下面这样:
if (n < 0)
printf("n is less than 0\n");
else if (n == 0)
printf("n is equal to 0\n");
else
printf("n is greater than 0\n");
因此,级联式 if
语句有自己独特的书写形式:
if (expr)
statment
else if (expr)
statment
...
else if (expr)
statment
else
statment
请记住
级联式 if
语句不是新的语句类型。它仅仅是普通的 if
语句,只是碰巧它的 else
子句又是一条新的 if
语句,以此类推。。。
程序实例
#include <stdio.h>
#include <stdlib.h>
int main() {
unsigned int a;
scanf("%u", &a);
if (a < 10) {
printf("个位\n");
} else if (a < 100) {
printf("⼗位\n");
} else if (a < 1000) {
printf("百位\n");
} else {
printf("很⼤\n");
}
return 0;
}
程序输出:
300↙️
百位
悬空else问题
当 if
语句嵌套时,我们需要当心臭名昭著的“悬空else”问题。思考下面这个例子:
if (y != 0)
if (x != 0)
result = x / y;
else
printf("Error: y is equal to 0\n");
上面else子句究竟属于哪一个if语句呢?缩进暗示它属于最外层的if语句。然而 C/C++ 遵循的规则是else子句应该属于离他最近的,且还没有和其他else匹配的if语句。因此,在这个例子中,else子句属于内层的if语句。
为了使else子句属于外层的if语句,我们可以用花括号将内层的if语句括起来:
if (y != 0) {
if (x != 0)
result = x / y;
} else
printf("Error: y is equal to 0\n");
DANGER
我们最好能规范if及else语句的写法,将每个if或else后面要执行的语句都使用大括号括起来。
条件表达式
C/C++ 提供了一种特殊的运算符——条件运算符,这种运算符可以根据条件产生两个值中的一个。条件运算符由 ?
和 :
组成,其格式如下:
expr1 ? expr2 : expr3
条件运算符是 C/C++ 中唯一一个由3个操作数的运算符,因此我们又把它称为。
条件表达式的求值步骤是:首先计算 expr1
的值,如果该值不为0,则计算 expr2
的值,并且可以把 expr2
的值当做整个表达式的值;如果 expr1
的值为0,那么计算 expr3
的值,并把 expr3
的值当做整个表达式的值。
请看下面的示例:
/**
* 复合运算符
*/
#include <stdio.h>
int main(int argc, char *argv[]) {
int i, j, k;
i = 1;
j = 2;
k = i > j ? i : j;
printf("k=%d\n", k); // k = j == 2
k = i > j ? i++ : j++;
printf("k=%d\n", k); // k = j == 2, 后置++ 先把j的值赋给k,然后自身再+1
k = (i >= 0 ? i : 0) + j; // k = i + j == 1 + 3 == 4, j的值在上次之后增加了1
printf("k=%d\n", k);
return 0;
}
程序运行结果:
k=2
k=2
k=4
顺便说一下,最后一条语句的圆括号的必须的,因为三目运算符的优先级只比赋值运算符的优先级高一点点。。。
程序实例
#include <stdio.h>
#include <stdlib.h>
int main() {
int a = 10;
int b = 20;
int c;
if (a > b) {
c = a;
} else {
c = b;
}
printf("c1 = %d\n", c);
a = 1;
b = 2;
c = (a > b ? a : b);
printf("c2 = %d\n", c);
return 0;
}
程序输出:
c1 = 20
c2 = 2
布尔值
在最初的 C 语言中,我们是用非零值表示 true
,用零值表示 false
。缺少布尔类型多多少少会带来一些麻烦。因此在 C99 中,定义了 _Bool
类型, _Bool
类型的变量只能赋值为0或者1。一般来说,往 _Bool
类型中存储非零值,会导致该变量赋值为1。
_Bool flag = 5; // flag is assigned 1
C99 中还提供了一个新的头文件 stdbool.h
,该头文件中定义了 bool
宏,用它来表示 _Bool
;而且该头文件中还定义了 true
和 false
两个宏,它们分别代表1和0。因此,以后我们可以这样写程序:
#include <stdbool.h>
...
bool flag = true;
switch语句
在日常编程中,常常需要把表达式和一些列值进行比较 从中找出匹配的值。前面可以看到,级联式 if
语句可以达到这个目的。
if (grade == 4)
printf("Excellent\n");
else if (grade == 3)
printf("Good\n");
else if (grade == 2)
printf("Average\n");
else if (grade == 1)
printf("Poor\n");
else if (grade == 0)
printf("Failing\n");
else
printf("Illegal grade\n");
C/C++ 提供了 switch
语句作为这类级联式 if
语句的替换。如上面例子可以写成这样:
switch (grade){
case 4:
printf("Excellent\n");
break;
case 3:
printf("Good\n");
break;
case 2:
printf("Average\n");
break;
case 1:
printf("Poor\n");
break;
case 0:
printf("Failing\n");
break;
default:
printf("Illegal grade\n");
break;
}
执行这条语句时,变量 grade
的值与 4,3,2,1 和 0 进行比较。如果值和 4 匹配,则打印 Excellent
,然后 break
语句会把控制传递给 switch
后边的语句。如果 grade
的值和列出的任何选项都不匹配,那么执行 default
分支, 则显示 Illegal grade
。
TIP
switch
语句往往比级联式 if
语句更容易阅读。此外,switch
语句的执行速度也会更快一些。
switch
语句最常用的格式如下:
switch (expr) {
case constant-expr:
statments;
break;
...
case constant-expr:
statments;
break;
default:
statments;
break;
}
switch
语句相对来说比较复杂,下面我们来看一下它的组成部分:
- 控制表达式
switch
后边表达式的值必须是整数类型。C/C++ 把字符类型也当做整数来处理,因此switch
语句也可以对字符类型进行判定。但是,不能判定浮点数和字符串(why?)。 - 分支标号
case
后边必须跟常量表达式,并且常量表达式的值必须是整数(字符类型也可以)。 - 语句 每个
case
后面可以跟任意数量的语句(不需要用花括号括起来)。每组语句的最后通常是一条break
语句。
常量表达式:即能够在编译期间求值的表达式。
程序实例
#include <stdio.h>
#include <stdlib.h>
int main() {
char c;
c = getchar();
switch (c)//参数只能是整型变量
{
case '1':
printf("OK\n");
break;//switch遇到break就中断了
case '2':
printf("not OK\n");
break;
default://如果上⾯的条件都不满⾜,那么执行default
printf("are u ok?\n");
}
return 0;
}
程序输出:
1↙️
OK
TIP
C/C++ 不允许有重复的分支标号,但对分支的顺序没有要求,特别是 default
分支不一定要放到最后。而且 switch
语句不要求一定有 default
分支。如果 default
不存在,而且控制表达式的值和任何一个分支标号都不匹配,控制会直接传递给 switch
后面的语句。
多个分支标号可以共用一组语句。如:
switch (grade) {
case 4: case 3: case 2: case 1:
printf("Passing\n");
break;
case 0:
printf("Failing\n");
break;
default:
printf("Illegal grade\n");
break;
}
case穿透
如果一个分支的最后没有 break
语句,那么控制会从一个分支继续到下一个分支,这种现象我们称之为 case
穿透。思考下面的语句:
switch (grade){
case 4:
printf("Excellent\n");
case 3:
printf("Good\n");
case 2:
printf("Average\n");
case 1:
printf("Poor\n");
case 0:
printf("Failing\n");
default:
printf("Illegal grade\n");
}
如果 grade 的值为 3,那么会显示什么呢?
Answer:
会把
switch
中,位于case 3
之后的pritntf
语句全部执行了,就像上面说的 “” 。
小练习
利用switch
语句编写一个程序,把用数字表示的成绩转化为字母表示的等级。
评定规则为:
- A 90~100
- B 80~89
- C 70~79
- D 60~69
- F 0~59
如果成绩高于100或者低于0,则显示出错消息。
程序代码
c/** * 练习题01 * * 利用`switch` 语句编写一个程序,把用数字表示的成绩转化为字母表示的等级。 */ #include <stdio.h> int main(int argc, char *argv[]) { printf("Enter grade: "); int grade = 0; scanf("%d", &grade); if (grade > 100 || grade < 0) { printf("Wrong grade\n"); return -1; } switch (grade / 10) { case 10: printf("letter grade A\n"); break; case 9: printf("letter grade A\n"); break; case 8: printf("letter grade B\n"); break; case 7: printf("letter grade C\n"); break; case 6: printf("letter grade D\n"); break; default: printf("letter grade F\n"); break; } return 0; }
运行结果