7.C语言的选择语句

前面已经讲过C语言的语句主要分为 6 大类。本节我们主要探讨 选择语句:if 语句 和 switch 语句

1.逻辑表达式

包括 if 语句在内的某些 C 语句(while,for 等)都必须测试表达式的值是“真”还是“假”。

许多编程语言中,类似 i < j 这样的表达式都具有特殊的“布尔”类型或者“逻辑”类型(C++ 的 bool 和 Java 的 boolean)。这样的类型只有两个值,即真(true)和假(false)。

而在 C 语言中,诸如 i < j 这样的比较会产生整数:0(假)1(真)。

但是,非 0 的其他数也可以表示 真。在今天看来,这是 C 语言设计的弊端,它将布尔类型与整型混为一谈,让我们在变成过程中可能稍不小心就会给自己挖一个坑。

1.1 关系运算符

C 语言的关系运算符(relational operator)和数学上的 >,<,≤,≥相对应,只是用在 C 语言的表达式中时产生的结果是 0 或 1 。

例如,表达式 10 < 11 的值是 1,11 < 10 的值是 0 。

关系运算符也可以用于比较整数和浮点数,也允许比较不同类型的操作数。如:5.6 < 5 的值为 0 。

符号

含义

<

小于

>

大于

<=

小于等于

>=

大于等于

关系运算符的优先级低于算数运算符。例如:i + j < k - 1 的意思是 (i + j) < (k - 1)

关系运算符都是左结合的。

注意: 表达式 i < j < k 在 C 语言中是合法的,但是可能不是你所期望的含义。因为 < 运算符是左结合的,所以这个表达式等价于:(i < j) < k

表达式会先检测 i 是否小于 j,然后用比较后产生的结果(1 或 0 )来和 k 进行比较。这个表达式并不是测试 j 是否位于 i 和 k 之间。(正确的写法是:j > i && j < k)

1.2 判等运算符

判等运算符(equality operator):相等用==表示 。注意不是 = ,=表示赋值。

注意:

一定要注意不要将 == 写成 = ,编译器可能会给你报错,但是如果没有,在你查错的时候,注意是不是 == 写错了的问题。

符号

含义

==

等于

!=

不等于

和关系运算符一样,判等运算符是左结合的,也是产生 0(假) 或 1(真) 作为结果。

判等运算符的优先级低于关系运算符。例如:

i < j == j < k 等价于:(i < j) == (j < k),含义是:如果 i < j 和 j < k 同真或同假 这个表达式的结果为真。

利用 关系运算符 和 判等运算符:

(i >= j) + (i == j)上面表达式的值为 0,1 或者 2 分别代表 i < j, i > j, i == j

1.3 逻辑运算符

逻辑运算符(logical operator)

符号

含义

!

逻辑非(一元运算符)

&&

逻辑与(二元运算符)

||

逻辑或(二元运算符)

其实 && 就是 数学中的 且 ,|| 就是数学的的 或

逻辑运算符产生的结果仍然是 0 或 1,操作数经常也是 0 或 1,但这不是必需的。逻辑运算符将任何 非0值操作数当作真来处理,任何0值操作数当作假来处理。

● 如果表达式的值为 0,那么 !表达式的结果为 1

● 如果 表达式1 和 表达式2 的值都是非零值,那么表达式1 && 表达式2 的结果为 1

● 如果 表达式1 和 表达式2 的值 中的任意一个是(或者两个都是)非零值,那么表达式1 || 表达式2 的结果为 1

所有其他情况下,这些运算符产生的结果都为 0

“短路”计算

&& 和 || 运算符都遵循“短路”原则。也就是说,这些运算符首先计算出左操作数的值,然后计算右操作数;如果表达式的值可以仅由左操作数推导出来,那么将不计算右操作数的值。如:

   int i = 0, j = 1;
 
    if (i && i++) {
        ; // 空语句
    }
    printf("%d\n", i); // i 的值没有增加,说明 i++ 没有计算
 
    if (j || j++) {
        ;
    }
    printf("%d\n", j); // j 的值没有增加, 说明 j++ 没有计算
// 输出:
0
1

运算符 !的优先级和一元正负号优先级相同,运算符 && 和 || 的优先级低于判等运算符。

例如:i < j && k == m 等价于 (i < j) && (k == m)

运算符 ! 是右结合的,&& 和 || 是左结合的。

2.  if 语句

2.1 if

if 语句允许程序通过测试表达式的值从两种选项中选择一种。if 语句的简单格式如下:

if(表达式){

   语句

}

如果语句部分只有一条语句,也可以写成

if(表达式)

   语句;

执行 if 语句时,先计算圆括号内表达式的值。如果表达式的值非零(C语言将非零值解释为真值),那么接着执行大括号内的语句。例如:

if(i > 0)

   printf("正数\n");

为了判定 k < i < j,可以这样写:

if(i > k && i < j)

为了判定相反的情况,可以写成:

if(i <= k || i >= j)

例2-1:程序:为了判定一个数是不是大于零的,如果是,我们就输出提示语,然后让这个数加 1

if(i > 0){
    printf("是正数\n");
    i++;
}

2.2 else 子句

if(表达式)

   语句;

else

   语句;如果是复合语句(compound statement),需要加上花括号

加上花括号是一种好习惯。建议不管是不是复合语句,尽量都加上花括号。

例2-2:增加需求:如果这个数不是正数,那么输出提示语,然后让这个数减 1

if(i > 0){
    printf("是正数\n");
    i++;
}else{
    printf("不是正数\n");
    i--;
}

2.3 嵌套的 if 语句

例2-3:找出 i,j,k 中的最大值,并将其保存到 max 中

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

2.4 级联式 if 语句

编程时常常需要判定一系列的条件,一旦其中某个条件为真就立刻停止。

如何做到呢?

例2-4 程序:判断 n 是大于 0 还是 等于 0 还是小于 0

使用 if else

if(n < 0){
    printf("n < 0");
}else{
    if(n == 0){
        printf("n = 0");
    }
    else{
        printf("n > 0");
    }
}

使用 else if

if(n < 0){
    printf("n < 0");
}
else if(n == 0){
    printf("n == 0");
}
else{
    printf("n > 0");
}

这样写可以避免 if else 嵌套,从而提高了书写和理解的难易度。

级联式 if 语句书写形式:

if(表达式){
    语句;   
}
else(表达式){
    语句;
}
else{
    语句;
}

2.5 “悬空 else”问题

请看下面的程序,思考 else 与 那个 if 匹配

if(y != 0)
    if(x != 0)
        printf("%.2f", x / y);
else
    printf("Error: y is zero!");

如果此时 y = 0, x = 2 会输出什么?

如果 y = 2, x = 0 会输出什么?

虽然缩进格式按时 else 属于外层 if,但是 C 语言遵循的规则是else 子句应该属于离它最近且还未和其他 else 匹配的 if 语句

所以,此例中 else 属于内层的 if 语句。为了避免这种问题,最好的办法就是加括号

if(y != 0){
    if(x != 0)
        printf("%.2f", x / y);
}
else
    printf("Error: y is zero!");

2.6. 条件表达式

条件运算符(conditional operator):C 语言运算符中唯一一个三元(ternary)(三个操作数)运算符。

格式:

[条件表达式]表达式 1 ? 表达式2 :表达式3 ;

例2-6

if(x > 0){
    x++;
}
else{
    x--;
}

上面的程序我们用条件运算符可以这么写:

x > 0 ? x++ : x--;判断 k 的值
i = 1;
j = 2;
k = i > j ? i : j; // k is 2 now
k = (i >= 0 ? i : 0) + j; // k is now 3

条件运算符使程序更短小但也更难以阅读,所以最好避免使用。然而有的情况会常常使用条件表达式。比如:

1)判断返回:

return i > j ? i : j;

2)printf

printf("%d\n", i > j ? i : j);

条件表达式也普遍应用于某些类型的宏定义中。

2.7 布尔值

C89

多年以来,C语言一直缺乏适当的布尔类型。

一种解决方法是,先声明一个 int 型变量,让后将其赋值为 0 或 1:

int flag;
flag = 0; // 表示 flag 为 false
...
flag = 1; // 表示 flag 为 true 

虽然这种方法可行,但是对于程序的可读性没有多大贡献,因为没有明确的表示 flag 的赋值只能是布尔型,并没有明确指出 0 和 1就是表示真和假。

为了使得程序更加便于理解,C89 程序员通常使用 TRUE 和 FALSE 这样的名字定义宏:

#define TRUE 1
#define FALSE 0

现在对 flag 的赋值就有了更加自然的形式:

flag = FALSE;
...
flag = TRUE;

为了判定 flag 是否为真,可以用:

if(flag == TRUE){
    ...
}

或者只写:

if(flag){
    ...
}

为了发扬这一思想,我们可以进一步定义一个用作布尔类型的宏:

#define BOOL int;声明布尔类型变量时可以用 BOOL 替代 int

BOOL flag;现在我们就非常清楚了:flag 不是一个普通的整型变量,而是表示布尔条件。(当然编译器还是将 flag 当作 int 类型的变量。)

C99

C99 中提供了 _Bool 类型:

_Bool flag;_

Bool是无符号整型。但是和一般的整形不同,_Bool 只能被赋值为 0 或 1 。一般来说,向 _Bool 类型变量中储存非零值会导致变量赋值为 1 。

_Bool flag = 5;
printf("%u", flag);
 
// 输出:
1

C99 还提供了一个新的头文件<stdbool.h>,改头提供了 bool 宏,用来代表 _Bool ;还提供了 true 和 false 两个宏,分别代表 1 和 0 。于是可以写:

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

3. switch 语句

日常的编程中,常常需要把表达式和一系列值进行比较,从而找出当前匹配的值。

使用级联式 if 语句可以达到这个目的:

if(grade == 4)
    printf("Excellent");
else if(grade == 3)
    printf("Good");
else if(grade == 2)
    printf("Average");
else if(grade == 1)
    printf("Poor");
else if(grade == 0)
    printf("Failing");
else
    printf("Illegal grade");

C 语言提供了 switch 语句作为这类级联式 if 语句的替换。使用 switch 语句改写上面的程序:

switch(grade){
    case 4: printf("Excellent"); 
            break;
    case 3: printf("Good");
            break;
    case 2: printf("Average");
            break;
    case 1: printf("Poor");
            break;
    case 0: printf("Failing");
            break;
    default:printf("Illegal grade");
            break;
}

switch 语句常用格式:

switch(控制表达式){
    case 常量表达式 : 语句
        ...
    case 常量表达式 : 语句
    default : 语句
}

● 控制表达式:控制表达式只能用:整型,字符型的变量(C 语言把字符当成整数来处理),不能用浮点数 和 字符串。

● 分支标号:每一个分支的开头都有一个标号,格式如下:

case 常量表达式;

常量表达式(constant expression): 必须是整数或字符型,不能包含变量和函数调用。

5 是常量表达式,5 + 10 也是常量表达式;但是 10 + n 不是常量表达式(除非 n 是表示常量的宏)。

● 语句:每个分支标号后可以跟任意数量的语句。不需要用花括号把这些语句括起来。每组语句的最后一条通常是 break 语句。

● break 的作用: 本节后面会详细讨论。

● default 语句的作用: 控制表达式的值和所有的标号语句都不匹配的话,会执行 default 后面的语句。(default :默认的意思)

C 语言不允许有重复的分支标号,但对分支的顺序没有要求,特别是 default 分支不一定要放在最后。

case 后只可以跟随一个常量表达式。但是,多个分支标号可以放置在同一组语句前面 。如:

switch(grade){
    case 4:
    case 3:
    case 2: 
    case 1: printf("Passing");
            break;
    case 0: printf("Failing");
            break;
    default:printf("Illegal grade");
            break;
}

为了节省空间,可以将拥有相同语句的分支标号放在同一行:

switch(grade){
    case 4: case 3: case 2:  case 1: 
            printf("Passing");
            break;
    case 0: printf("Failing");
            break;
    default:printf("Illegal grade");
            break;
}

switch 语句不要求一定有 default 分支。如果 default 不存在,而且控制表达式的值和所有的标号语句都不匹配的话,控制会直接传给 switch 语句后面的语句。

break 语句的作用

break 会使程序“跳出” switch 语句,继续执行 switch 后面的语句。

对控制表达式求值的时,控制会跳转到与 switch 表达式相匹配的分支标号处。分支标号只是说明 switch 内部位置的标记。在执行完分支的最后一句后,程序控制“向下跳转”到下一个分支的第一条语句上,而忽略下一个分支的分支标号。如果没有 break 语句(或者其他某种跳转语句),控制将会从一个分支继续到下一个分支。思考下面的 switch 语句:

switch(grade){
    case 4: printf("Excellent"); 
    case 3: printf("Good");
    case 2: printf("Average");
    case 1: printf("Poor");
    case 0: printf("Failing");
    default:printf("Illegal grade");
}

如果 grade 的值为 3,那么显示的信息是:

GoodAveragePoorFailingIllegal grade

注意:忘记 break 是编程时常见的错误。虽然有时候会故意忽略 break 以便多个分支共享代码,但是通常情况下省略 break 是因为忽略。

如果有需要,明确指出故意省略 break 语句是一个好主意:

switch(grade){
    case 4: case 3: case 2:  case 1: 
            num_passing++;
        // Fail Through 
    case 0: total_grades++;
            break;
}

最然 switch 语句最后一个分支不需要 break 语句,但是通常还是会加上一个 break 语句在那里,以防止将来增加分支时出现“丢失” break 的问题。

  • 30
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shangjg3

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值