C语言入门(四)——分支语句

本文介绍了C语言中的条件控制结构,包括if语句和if/else语句的使用,以及布尔代数在逻辑运算中的应用。此外,还讨论了switch语句的语法和特点,强调了break语句在控制流程中的作用。文章强调了在编写条件表达式时需要注意的事项,如运算符的优先级和类型匹配,并提醒开发者注意浮点数的不精确性。
摘要由CSDN通过智能技术生成

if语句

if/else语句

布尔代数

switch语句


if语句

目前我们写的简单函数中可以有多条语句,但这些语句总是从前到后顺序执行的。除了顺序执行之
外,有时候我们需要检查一个条件,然后根据检查的结果执行不同的后续代码,在 C 语言中可以用
分支语句( Selection Statement )实现,比如:
if (x != 0) {
 printf("x is nonzero.\n");
}
其中 x != 0 表示 “x 不等于 0” 的条件,这个表达式称为控制表达式( Controlling Expression )如果条
件成立,则 {} 中的语句被执行,否则 {} 中的语句不执行,直接跳到 } 后面。 if 和控制表达式改变了程
序的控制流程( Control Flow ),不再是从前到后顺序执行,而是根据不同的条件执行不同的语
句,这种控制流程称为分支( Branch )。上例中的 != 号表示 不等于 ,像这样的运算符有:
关系运算符和相等性运算符
运算符含义
==等于
!=不等于
>大于
<小于
>=大于或等于
<=小于或等于
注意以下几点:
1.这里的 == 表示数学中的相等关系,相当于数学中的 = 号,初学者常犯的错误是在控制表达式中把== 写成 = ,在 C 语言中 = 号是赋值运算符,两者的含义完全不同。
2.如果表达式所表示的比较关系成立则值为真( True ),否则为假( False ),在 C 语言中分别
int 型的 1 0 表示。如果变量 x 的值是 -1 ,那么 x>0 这个表达式的值为 0 x>-2 这个表达式的
值为 1。
3.在数学中a<b<c表示b既大于a又小于c,但作为C语言表达式却不是这样。以上几种运算符都是
左结合的,请读者想一下这个表达式应如何求值。
4.这些运算符的两个操作数应该是相同类型的,两边都是整型或者都是浮点型可以做比较,但
两个字符串不能做比较,在后面我们会介绍比较字符串的方法。
5.==和!=称为相等性运算符(Equality Operator),其余四个称为关系运算符(Relational
Operator ),相等性运算符的优先级低于关系运算符。
总结一下, if (x != 0) { ... } 这个语句的计算顺序是:首先求 x != 0 这个表达式的值,如果值为0 ,就跳过 {} 中的语句直接执行后面的语句,如果值为 1 ,就先执行 {} 中的语句,然后再执行后面的语句。事实上控制表达式取任何非0 值都表示真值,例如 if (x) { ... } if (x != 0) { ...}是等价的,如果 x 的值是 2 ,则 x != 0 的值是 1 ,但对于 if 来说不管是 2 还是 1 都表示真值。
if 语句相关的语法规则如下:
语句 → if ( 控制表达式 ) 语句
语句 → { 语句列表}
语句 → ;
C 语言中,任何允许出现语句的地方既可以是由 ; 号结尾的一条语句,也可以是由 {} 括起来的若干
条语句或声明组成的语句块( Statement Block ),语句块和上一章介绍的函数体的语法相同。注意语句块的} 后面不需要加 ; 号。如果 } 后面加了 ; 号,则这个 ; 号本身又是一条新的语句了,在 C 语言中一个单独的; 号表示一条空语句( Null Statement )。上例的语句块中只有一条语句,其实没必要写成语句块,可以简单地写成:
if (x != 0)
 printf("x is nonzero.\n");
语句块中也可以定义局部变量,例如:
void foo(void)
{
 int i = 0;
 {
 int i = 1;
 int j = 2;
 printf("i=%d, j=%d\n", i, j);
 }
 printf("i=%d\n", i); /* cannot access j here */
}
和函数的局部变量同样道理,每次进入语句块时为变量 j 分配存储空间,每次退出语句块时释放变
j的存储空间。语句块也构成一个作用域,和 作用域的分析类似,如果整个源文件是一张
大纸, foo 函数是盖在上面的一张小纸,则函数中的语句块是盖在小纸上面的一张更小的纸。语句
块中的变量 i 和函数的局部变量 i 是两个不同的变量,因此两次打印的 i 值是不同的;语句块中的变
j 在退出语句块之后就没有了,因此最后一行的 printf 不能打印变量 j ,否则编译器会报错。语句
块可以用在任何允许出现语句的地方,不一定非得用在 if 语句中,单独使用语句块通常是为了定义
一些比函数的局部变量更 局部 的变量。

if/else语句

if 语句还可以带一个 else 子句( Clause ),例如:
if (x % 2 == 0)
 printf("x is even.\n");
else
 printf("x is odd.\n");
这里的 % 是取模( Modulo )运算符, x%2 表示 x 除以 2 所得的余数( Remainder ), C 语言规定 %
算符的两个操作数必须是整型的。两个正数相除取余数很好理解,如果操作数中有负数,结果应该
是正是负呢? C99 规定,如果 a b 是整型, b 不等于 0 ,则表达式 (a/b)*b+a%b 的值总是等于 a,再结表达式讲过的整数除法运算要Truncate Toward Zero,可以得到一个结论: %运算符的结果总是与被除数同号(想一想为什么)。其它编程语言对取模运算的规定各不相同,也有规定结果和除数同号的,也有不做明确规定的。
取模运算在程序中是非常有用的,例如上面的例子判断 x 的奇偶性( Parity ),看 x 除以 2 的余数是不
0 ,如果是 0 则打印 x is even. ,如果不是 0 则打印 x is odd. ,读者应该能看出 else 在这里的作用
了,如果在上面的例子中去掉 else ,则不管 x 是奇是偶, printf("x is odd.\n"); 总是执行。为了
让这条语句更有用,可以把它封装( Encapsulate )成一个函数:
void print_parity(int x)
{
 if (x % 2 == 0)
 printf("x is even.\n");
 else
 printf("x is odd.\n");
}
把语句封装成函数的基本步骤是:把语句放到函数体中,把变量改成函数的参数。这样,以后要检
查一个数的奇偶性只需调用这个函数而不必重复写这条语句了,例如:
print_parity(17);
print_parity(18);
if/else 语句的语法规则如下:
语句 → if ( 控制表达式 ) 语句 else 语句
右边的 语句 既可以是一条语句,也可以是由 {} 括起来的语句块。一条 if 语句中包含一条子语句,
一条 if/else 语句中包含两条子语句,子语句可以是任何语句或语句块,当然也可以是另外一
if if/else 语句。根据组合规则, if if/else 可以嵌套使用。例如可以这样:
if (x > 0)
 printf("x is positive.\n");
else if (x < 0)
 printf("x is negative.\n");
else
 printf("x is zero.\n");
也可以这样:
if (x > 0) {
 printf("x is positive.\n");
} else {
 if (x < 0)
 printf("x is negative.\n");
 else
 printf("x is zero.\n");
}
现在有一个问题,类似 if (A) if (B) C; else D; 形式的语句怎么理解呢?可以理解成
if (A)
 if (B)
        C;
else
   D;

也可以理解成

if (A)
  if (B)
        C;
  else
        D;
1 继续 Hello World” 讲过, C 代码的缩进只是为了程序员看起来方便,实际上对编译器不起
任何作用,你的代码不管写成上面哪一种缩进格式,在编译器看起来都是一样的。那么编译器到底
按哪种方式理解呢?也就是说, else 到底是和 if (A) 配对还是和 if (B) 配对?很多编程语言的语法
都有这个问题,称为 Dangling-else 问题。 C 语言规定, else 总是和它上面最近的一个 if 配对,因此
应该理解成 else if (B) 配对,也就是按第二种方式理解。如果你写成上面第一种缩进的格式就很
危险了:你看到的是这样,而编译器理解的却是那样。如果你希望编译器按第一种方式理解,应该
明确加上 {}
if (A) {
 if (B)
   C;
} else
   D;
顺便提一下,浮点型的精度有限,不适合用 == 运算符做精确比较。以下代码可以说明问题:
double i = 20.0;
double j = i / 7.0;
if (j * 7.0 == i)
 printf("Equal.\n");
else
 printf("Unequal.\n");
不同平台的浮点数实现有很多不同之处,在我的平台上运行这段程序结果为 Unequal ,即使在你的
平台上运行结果为 Equal ,你再把 i 改成其它值试试,总有些值会使得结果为 Unequal 。等学习了 4 浮点数 你就知道为什么浮点型不能做精确比较了。

布尔代数

1 “if 语句 讲过, a<b<c 不表示 b 既大于 a 又小于 c ,那么如果想表示这个含义该怎么写呢?可
以这样:
if (a < b) {
 if (b < c) {
 printf("b is between a and c.\n");
 }
}
我们也可以用逻辑与( Logical AND )运算符表示这两个条件同时成立。逻辑与运算符在 C 语言中写成两个& 号( Ampersand ),上面的语句可以改写为:
if (a < b && b < c) {
 printf("b is between a and c.\n");
}
对于 a < b && b < c 这个控制表达式,要求 a < b 的值非 0” b < c 的值非 0” 这两个条件同时成立
整个表达式的值才为 1 ,否则整个表达式的值为 0 。也就是只有两个条件都为真,它们做逻辑与运算
的结果才为真,有一个条件为假,则逻辑与运算的结果为假,如下表所示:
ABA AND B
000
010
100
111
这种表称为真值表( Truth Table )。注意逻辑与运算的操作数以非 0 表示真以 0 表示假,而运算结果
1 表示真以 0 表示假(类型是 int ),我们忽略这些细微的差别,在表中全部以 1 表示真以 0 表示 假。C 语言还提供了逻辑或( Logical OR )运算符,写成两个 | 线( Pipe Sign ),逻辑非( Logical
NOT )运算符,写成一个 ! 号( Exclamation Mark ),它们的真值表如下:
ABA OR B
000
011
101
111
逻辑或表示两个条件只要有一个为真,它们做逻辑或运算的结果就为真,只有两个条件都为假,逻
辑或运算的结果才为假。逻辑非的作用是对原来的逻辑值取反,原来是真的就是假,原来是假的就
是真。逻辑非运算符只有一个操作数,称为单目运算符 Unary Operator ),以前讲过的加减乘 除、赋值、相等性、关系、逻辑与、逻辑或运算符都有两个操作数,称为双目运算符(Binary
Operator )。
关于逻辑运算的数学体系称为布尔代数 Boolean Algebra),以它的创始人布尔命名。在编程语
言中表示真和假的数据类型叫做布尔类型,在 C 语言中通常用 int 型来表示,非 0 表示真, 0 表示假[ 6 ] 。布尔逻辑是写程序的基本功之一,程序中的很多错误都可以归因于逻辑错误。以下是一些布尔代数的基本定理,为了简洁易读,真和假用1 0 表示, AND * 号表示, OR + 号表示(从真值表可以看出AND OR 运算确实有点像乘法和加法运算), NOT ¬ 表示,变量 x y z 的值可能是0 也可能是 1

 除了第1行之外,这些公式都是每两行一组的,每组的两个公式就像对联一样:把其中一个公式中 的*换成++换成*0换成11换成0,就变成了与它对称的另一个公式。这些定理都可以通过真 值表证明,更多细节可参考有关数字逻辑的教材,例如[数字逻辑基础]

目前为止介绍的这些运算符的优先级顺序是: ! 高于 * / % ,高于 + - ,高于 > < >= <= ,高于 == !=
高于 && ,高于 || ,高于 = 。写一个控制表达式很可能同时用到这些运算符中的多个,如果记不清楚
运算符的优先级一定要多套括号。我们将在 运算符总结 总结 C 语言所有运算符的优先级和结合性。

switch语句

switch 语句可以产生具有多个分支的控制流程。它的格式是:
switch ( 控制表达式 ) {
case 常量表达式: 语句列表
case 常量表达式: 语句列表
...
default : 语句列表
}
例如以下程序根据传入的参数 1~7 分别打印 Monday~Sunday

如果传入的参数是 2 ,则从 case 2 分支开始执行,先是打印相应的信息,然后遇到 break 语句,它的
作用是跳出整个 switch 语句块。 C 语言规定各 case 分支的常量表达式必须互不相同,如果控制表达
式不等于任何一个常量表达式,则从 default 分支开始执行,通常把 default 分支写在最后,但不是
必须的。使用 switch 语句要注意几点:
1.case后面跟表达式的必须是常量表达式,这个值和全局变量的初始值一样必须在编译时计算
出来。
2. 2 “if/else 语句 讲过浮点型不适合做精确比较,所以C语言规定case后面跟的必须是 整型
常量表达式。
3.进入case后如果没有遇到break语句就会一直往下执行,后面其它case或default分支的语句
也会被执行到,直到遇到 break ,或者执行到整个 switch 语句块的末尾。通常每个 case 后面都
要加上 break 语句,但有时会故意不加 break 来利用这个特性,例如:

 

switch 语句不是必不可缺的,显然可以用一组 if ... else if ... else if ... else ... 代替,
但是一方面用 switch 语句会使代码更清晰,另一方面,有时候编译器会对 switch 语句进行整体优
化,使它比等价的 if/else 语句所生成的指令效率更高。
  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

接受平凡 努力出众

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

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

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

打赏作者

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

抵扣说明:

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

余额充值