Skip to content

选择语句

选择语句分为 if 语句和 switch 语句。

if语句

if 语句最简单的格式为:

cpp
if (expr) statment

比如下面这个示例

cpp
if (line_num == MAXLINES)
    line_num = 0;

注意

不要混淆 ===。语句 if (i == 0) ... 测试i是否等于0;而语句 if (i = 0) ... 则是先把0赋值给i,然后测试表达式的值是否非零,在这种情况下测试总是失败的。

程序实例

c
#include <stdio.h>
#include <stdlib.h>

int main() {
    int a = 5;
    int b = 2;

    if (a > b) {
        printf("%d\n", a);
    }

    return 0;
}

程序输出:

shell
5

复合语句

如果我们想用 if 语句控制两条或者更多条语句,该怎么办呢?

这时就需要引入复合语句了。

复合语句格式如下:

cpp
{ statment }

通过将多条语句用花括号括起来,可以强制编译器将其作为一条语句来处理。如:

cpp
{
    line_num = 0;
    ++page_num;
}

这样我们就可以在 if 语句中使用复合语句了:

cpp
if (line_num == MAXLINES) {
    line_num = 0;
    ++page_num;
}

else子句

if 语句还可以有 else 子句,其格式为

cpp
if (expr) {
    statment
} else {
    statment
}

如果 expr 的值为0,那么就执行 else 子句。如:

cpp
if (i > j){
    max = i;
} else {
    max =j;
}

C/C++对 if 语句内部的语句没有任何限制。事实上,在 if 语句内部嵌套其他 if 语句是非常普遍的。比如,我们可以用下面的语句找出三个值中的最大值:

cpp
if (i > j) {
    if (i > k){
        max = i;
    } else {
        max = k;
    }
} else {
    if (j > k){
        max = j;
    } else {
        max = k;
    }
}

程序实例

c
#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;
}

程序输出:

shell
2

级联式if语句

写程序时,我们经常需要判定一系列条件,直到某个条件为真。级联式 if 语句往往是编写这类程序的最好方法。如:

cpp
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 语句进行缩进,而是写成下面这样:

cpp
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 语句有自己独特的书写形式:

cpp
if (expr)
    statment
else if (expr)
    statment
...
else if (expr)
    statment
else
    statment

请记住

级联式 if 语句不是新的语句类型。它仅仅是普通的 if 语句,只是碰巧它的 else 子句又是一条新的 if 语句,以此类推。。。

程序实例

c
#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;
}

程序输出:

shell
300↙️
百位

悬空else问题

if 语句嵌套时,我们需要当心臭名昭著的“悬空else”问题。思考下面这个例子:

cpp
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语句括起来:

cpp
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++ 提供了一种特殊的运算符——条件运算符,这种运算符可以根据条件产生两个值中的一个。条件运算符由 ?: 组成,其格式如下:

cpp
expr1 ? expr2 : expr3

条件运算符是 C/C++ 中唯一一个由3个操作数的运算符,因此我们又把它称为

条件表达式的求值步骤是:首先计算 expr1 的值,如果该值不为0,则计算 expr2 的值,并且可以把 expr2 的值当做整个表达式的值;如果 expr1 的值为0,那么计算 expr3 的值,并把 expr3 的值当做整个表达式的值。

请看下面的示例:

c
/**
 * 复合运算符
*/

#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;
}

程序运行结果:

shell
k=2
k=2
k=4

顺便说一下,最后一条语句的圆括号的必须的,因为三目运算符的优先级只比赋值运算符的优先级高一点点。。。

程序实例

c
#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;
}

程序输出:

shell
c1 = 20
c2 = 2

布尔值

在最初的 C 语言中,我们是用非零值表示 true,用零值表示 false。缺少布尔类型多多少少会带来一些麻烦。因此在 C99 中,定义了 _Bool 类型, _Bool 类型的变量只能赋值为0或者1。一般来说,往 _Bool 类型中存储非零值,会导致该变量赋值为1。

cpp
_Bool flag = 5;     // flag is assigned 1

C99 中还提供了一个新的头文件 stdbool.h,该头文件中定义了 bool 宏,用它来表示 _Bool;而且该头文件中还定义了 truefalse 两个宏,它们分别代表1和0。因此,以后我们可以这样写程序:

cpp
#include <stdbool.h>
...
bool flag = true;

switch语句

在日常编程中,常常需要把表达式和一些列值进行比较 从中找出匹配的值。前面可以看到,级联式 if 语句可以达到这个目的。

cpp
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 语句的替换。如上面例子可以写成这样:

cpp
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 语句最常用的格式如下:

cpp
switch (expr) {
    case constant-expr:
        statments;
        break;
...
    case constant-expr:
        statments;
        break;
    default:
        statments;
        break;
}

switch 语句相对来说比较复杂,下面我们来看一下它的组成部分:

  • 控制表达式 switch 后边表达式的值必须是整数类型。C/C++ 把字符类型也当做整数来处理,因此switch 语句也可以对字符类型进行判定。但是,不能判定浮点数和字符串(why?)。
  • 分支标号 case 后边必须跟常量表达式,并且常量表达式的值必须是整数(字符类型也可以)。
  • 语句 每个 case 后面可以跟任意数量的语句(不需要用花括号括起来)。每组语句的最后通常是一条 break 语句。

常量表达式:即能够在编译期间求值的表达式。

程序实例

c
#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;
}

程序输出:

shell
1↙️
OK

TIP

C/C++ 不允许有重复的分支标号,但对分支的顺序没有要求,特别是 default 分支不一定要放到最后。而且 switch 语句不要求一定有 default 分支。如果 default 不存在,而且控制表达式的值和任何一个分支标号都不匹配,控制会直接传递给 switch 后面的语句。

多个分支标号可以共用一组语句。如:

cpp
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 穿透。思考下面的语句:

cpp
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;
}

运行结果