C语言K&R圣经笔记 2.11条件表达式 2.12优先级和求值顺序

2.11条件表达式

复制代码
 if (a > b) 
     z = a; 
 else 
     z = b; 

上面的语句计算 a 和 b 中的最大值并存入 z。而使用三元操作符 ? : 的条件表达式,为这个结构及类似结构提供了另一种写法。在如下表达式

复制代码
expr1 ? expr2 : expr3

中,首先对 expr1 求值。如果值非0 (为真),则对 expr2 求值,而得到的也是整个条件表达式的值。若 exp1 的值为0(为假),则对 expr3 求值,得到整个表达式的值。 expr2 和 expr3 中只会有一个表达式被求值。因此,把 z 设为 a 和 b 的最大值就能写成:

复制代码
z = a > b ? a : b;    /* z = max(a,b) */

需要注意,条件表达式确实是一个表达式,它能出现在任何其他表达式可以出现的地方。如果 expr2 和 expr3 的类型不同,则结果的类型取决于本章前面讨论过的转换规则。例如, 如果 f 是 float 而 n 是 int,则表达式

复制代码
(n > 0) ? f : n

的类型是 float, 不管 n 是否大于 0。

条件表达式的第一个表达式两边不用加上括号,因为 ?: 的优先级非常低,仅高于赋值。然而加上括号是明智的,因为这会让条件表达式的条件部分看起来更明显。

条件表达式可以带来更简洁的代码。例如,下面这个循环会打印数组的 n 个元素,每行10个,中间用空格隔开,每行(包括最后一行)末尾以换行符结束。

复制代码
for (i = 0; i < n; ++i)
    printf("%6d%c", a[i], (i%10==9 || i== n-1) ? '\n' : ' ');

每10个元素后面打印一个换行符,第n个元素后面也要。其他所有元素后面打印空格。代码可能看起来有点绕,但比对应的 if-else 写法要紧凑。还有个不错的例子是

复制代码
printf("You have %d items%s.\n", n, n==1 ? "" : "s"); 

练习2-10、重写 把大写字母转小写的 lower 函数,用条件表达式,不用 if-else。

2.12优先级和求值顺序

表2-1总结了所有操作符的优先级和结合性,包括那些我们还没讲到的。同一行的操作符有相同的优先级;行按优先级降序排列,例如 操作符 * / % 的优先级相同,而它们三个的优先级都比二元操作符 + - 要高。括号"操作符" ( ) 指的是函数调用。操作符 -> 和 . 用来访问结构的成员,还有 sizeof(对象的大小),都会在第6章讲到。第5章会讨论 * (通过指针引用)和 & (对象的地址),而第3章会讨论逗号操作符。

注意位操作符 & ^ | 优先级低于 == 和 !=。这隐含说明,用来测试位的表达式如

复制代码
if ((x & MASK) == 0) ...

里面必须全部用括号包起来,才能得到想要的结果。

C,和大多数语言一样,并没有指定一个操作符中多个操作数的求值顺序。(操作符 && || ?: 和逗号操作符除外)。例如语句

复制代码
x = f() + g()

f 可能 在 g 之前求值,可能相反;这样如果 f 或 g 改变了对方依赖的一个变量值,则 x 的值依赖于求值顺序。应对这种情况,可以用临时变量来保存中间结果,以保证所需的顺序。

类似的,函数的多个参数的求值顺序也是没有规定的,因此语句

复制代码
printf("%d %d\n", ++n, power(2,n));    /* WRONG */

用不同的编译器可以产生不同的结果,依赖于 n 是否在 power 被调用之前自增。当然,解决方法是写成

复制代码
++n;
printf("%d %d\n", n, power(2,n));

函数调用,嵌套的赋值语句,以及自增和自减操作符,都会带来"副作用"------作为表达式求值的副产品,某些变量被改变了。在任何涉及副作用的表达式中,都可能存在对表达式中变量的更新顺序的微妙依赖。这种代码令人难受。来看一个典型的:

复制代码
a[i] = i++;

问题是,数组下标用的是 i 的老值还是新值?不同的编译器可以用不同的方式来解释,并基于不同的解释生成不同的结果。标准有意地不明确规定大部分的这类问题。表达式中的副作用(对变量赋值)何时发生,这个决定权交给了编译器,因为最佳的选择强烈依赖于机器架构。(标准的确规定了,所有参数的副作用生效在函数被调用之前,但这无法解决上面的 printf 问题。)

结论就是:不管在任何语言中,写出依赖于求值顺序的代码都是糟糕的编程实践。自然地,有必要知道应该避免什么,但如果你不知道在各种不同的机器上是分别如何实现的,你也不会忍不住去利用某种特定的实现。

(第二章完)

相关推荐
talen_hx2966 分钟前
OkHttpClient的最佳实践优化方案
java·笔记·学习
三品吉他手会点灯12 分钟前
C语言学习笔记 - 26.C编程预备计算机专业知识 - 15~25关键内容回顾
c语言·笔记·学习
聆风吟º13 分钟前
【C标准库】深入理解C语言pow函数:从入门到精通,一文搞定幂运算
c语言·开发语言·库函数·pow·幂运算
流年如夢28 分钟前
顺序表(LeetCode)
c语言·数据结构·leetcode·职场和发展
许长安10 小时前
RPC 同步调用基本使用方法:基于官方 RouteGuide 示例
c++·经验分享·笔记·rpc
SunnyByte12 小时前
C语言——贪吃蛇的实现
c语言·单链表·贪吃蛇
做cv的小昊14 小时前
【TJU】研究生应用统计学课程笔记(8)——第四章 线性模型(4.1 一元线性回归分析)
笔记·线性代数·算法·数学建模·回归·线性回归·概率论
我命由我1234515 小时前
程序员的心理学学习笔记 - 空杯心态
经验分享·笔记·学习·职场和发展·求职招聘·职场发展·学习方法
晓梦林15 小时前
3170靶场学习笔记
笔记·学习
ZC跨境爬虫15 小时前
跟着 MDN 学 HTML day_17:媒体与 Web Audio API 自动播放指南——策略、检测与最佳实践
前端·笔记·ui·html·音视频·媒体