摘要
C++ 的表达式可以做两件不同的事:给出一个值,以及改变程序的状态。x + 1 只给出一个值;x = 7 给出一个值,此外还改变 x;x++ 给出 x 原来的值,并在 x 中留下另一个值。本课研究表达式的值与作用之间的这一区别:作为表达式的赋值、复合赋值、前缀与后缀形式的自增和自减,以及表达式各部分的求值顺序,而标准对这一顺序只作了部分规定。由此引出贯穿本课其余部分的问题:标准对一个程序承诺了什么。答案是一个分为四个等级的目录,从良定义行为到未定义行为,它汇集了到目前为止见过的所有情形,并对每一种说明由谁来发现它(编译器、检测器、库),以及哪些情形谁也发现不了。
学习目标
- 在赋值、自增和函数调用中,区分表达式的值与它的副作用。
- 预测
x++和++x作为值使用时的结果,并手工执行一个含有复合赋值的程序。 - 识别那些在标准未规定顺序的情况下两次修改同一变量、或者既读取又修改同一变量的表达式,并把它们改写成独立的语句。
- 把一个构造归类为良定义、实现定义、未指明或未定义,每一类各举一例。
- 对目录中的每一种未定义行为,说出编译器是否对它发出警告,检测器或库是否让它停下,还是谁也发现不了。
值与作用
算术运算一课把表达式看作一棵归约为一个值的树:2 + 3 * 4 的值是 14,计算它不会改变表达式本身以外的任何东西。关系与逻辑一课对条件做了同样的事,条件的值是 true 或 false。然而,有些表达式除了给出一个值之外,还会在程序的状态上留下痕迹:改变一个变量的值、往屏幕上写东西,或者从输入读取。这种痕迹称为副作用,或者简称作用;“副”字并不表示它不那么重要:在 x = 7 中,作用,即让 x 变为 7,恰恰就是我们想要的。
若是如此,每个表达式都应当带着两个问题来读:它给出什么,它做什么。三个例子可以把这一区别固定下来。count + 1 给出 count 的后继,什么也不做:count 保持原值。count = 9 给出 9,并让 count 变为 9。std::println("你好") 也是一个表达式,即一次函数调用,它重要的是作用:写出一行。自己编写的函数将在函数一课中学习,届时这一区别会再次出现:只计算结果的函数,比还会改变自身以外某些东西的函数更容易理解,也更容易测试。
像 count + 1; 这样的语句是合法的 C++:值被计算出来然后丢弃,因此毫无用处,GCC 会用 -Wunused-value 对此发出警告。表达式语句的意义在于它的作用。由此可知,带作用的表达式是推动程序前进的表达式,也是需要小心对待的表达式,因为作用发生在某个时刻,而这个时刻并不总是读者所设想的那一个。
正在加载内容…
已完成
登录账户以保存您的进度