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 问题。)

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

(第二章完)

相关推荐
三十岁老牛再出发6 小时前
07.27&7.28每日总结
c语言·mysql
一只小菜鸡..7 小时前
南京大学 操作系统 (JYY) 学习笔记:从 UNIX 到 Linux 与庞大的应用生态
笔记·学习·unix
W_326008 小时前
VSCode 进阶
c语言·vscode
麻瓜老宋9 小时前
AI开发C语言应用按步走,表达式计算器calc的第二十三步,多行输入、进制输出、错误恢复、常量折叠、配置加载等
c语言·开发语言·atomcode
熊猫钓鱼>_>10 小时前
ArkTS 装饰器总览:V1 / V2 / 通用装饰器完整学习笔记
笔记·学习·华为·交互·harmonyos·arkts·arkweb
GLDbalala10 小时前
GPU PRO 5 - 5.2 glTF: Designing an Open-Standard Runtime Asset Format笔记
笔记
春生野草10 小时前
个人笔记——栈
笔记
chnyi6_ya10 小时前
论文阅读笔记 | One Sentence, One Drama: 基于多智能体系统的个性化短剧生成
论文阅读·笔记
ysa05103011 小时前
【板子】短序列dp(换成维护更小常数维度的dp)
c++·笔记·算法·板子
Dory_Youth12 小时前
联想笔记本电脑失灵
运维·笔记·电脑