6.5 表达式
1、一个表达式是操作符与操作数的一个序列,这些操作符与操作数指定了一个值的计算,或指派一个对象或一个函数,或是生成副作用,或执行上述操作的组合。对一个操作符的操作数的值计算顺序排在对该操作符的结果的值计算之前。[译者注:比如
int a = (100 + 5) * (20 - 6);
对于上述表达式,就对于当中的 * 操作符而言,先计算乘法操作符左右两边的操作数的计算,最后再计算乘法操作结果。
]
2、如果在一个标量对象上的副作用相对于同一标量对象上的另一个不同副作用或是使用同一标量对象的值的一个值计算,没有为其编排前后次序,那么行为是未定义的。如果一个表达式有多个可允许的子表达式的次序,那么行为是未定义的,如果这么一个未安排次序的副作用以任一次序发生的话。[注:以下代码描述了未定义的语句表达式:
i = ++i + 1; a[i++] = i;
以下是允许的:
i = i + 1; a[i] = i;
]
3、操作符与操作数的组合通过语法来指定。[注:语法指定了一个表达式计算中操作符的优先级,这与本子条款的主要子条款的次序相同,最高优先级先被介绍。从而,比如,允许作为双目操作符 + 的表达式(6.5.6)是定义在6.5.1到6.5.6中的那些表达式。]例外是投射表达式(6.5.4)作为单目操作符(6.5.3)的操作数,以及跟在任一下列操作符对之后的其中间所包含的一个操作数:用作组合的小括号 ( ) (6.5.1),下标中括号 [ ] (6.5.2.1),函数调用小括号 ( ) (6.5.2.2),以及条件操作符 ? : (6.5.15)。在每个主条款中,操作符具有相同的优先级。左或右结合性在每个子条款中被指明,通过用于在里面所讨论的表达式的语法。]除了稍后指定的,副作用与子表达式的值计算不被按次序编排的情况。[注:在一个表达式中,该表达式在一个程序的执行期间被执行多次,并且没有被安排前后次序,对其子表达式的计算也具有不确定的前后次序,那么该表达式不需要在不同计算中执行相一致。]
4、某些操作符(单目操作符 ~ ,以及双目操作符 << , >>,& , ^ ,以及 | ,统称为按位操作符)要求具有整数类型的操作数。这些操作符依赖整数的内部表示来产生值,并且具有对于带符号类型以实现定义的及未定义的方面。[译者注:尤其对于 >> 和 << 作用在带符号整数类型上,不同处理器架构具有不同行为。]
5、如果在一个表达式的计算期间有一个意外情况发生(即,如果结果不是数学上定义的,也不是在其类型可表示的值范围内的),那么行为是未定义的。
6、一个对象的有效类型,用于访问其存储的值,是该对象所声明的类型,如果存在的话。[注:动态分配的对象不具有被声明的类型。]如果一个值被存储在不具有声明类型的一个对象中,通过某个不是一个字符类型的左值,那么该左值的类型变为该对象的有效类型,用于访问以及用于后续访问不修改所存储的值。如果一个值通过memcpy或memmove被拷贝到一个不具有声明类型的对象中,或是作为一个字符类型的数组进行拷贝,那么用于访问以及后续不修改值的访问的被修改对象的有效类型是值所被拷贝的源对象的有效类型,如果源对象有有效类型的话。对于所有其它对一个不具有声明类型的对象的访问,该对象的有效类型即为用于访问的左值的类型。
7、一个对象应该让其所存储的值仅通过一个左值表达式来访问,该左值表达式具有以下类型之一:[注:这个列表的目的是用于指定那些在一个对象可以或不可以被别名化的情况。]
——与该对象的有效类型相兼容的一个类型,
——与该对象的有效类型相兼容的一个类型的一个限定版本,
——相应于该对象有效类型的一个带符号或无符号类型,
——相应于该对象的有效类型的一个限定版本的一个带符号或无符号类型
——一个聚合或联合类型,在其成员中包含上述所提到的类型之一(递归地包括一个子集的或所包含的联合体的一个成员),或
——一个字符类型。
8、一个浮点表达式可以被抽象(contracted),也就是说,对一个浮点数的计算就好比是一单次操作,而忽略由源代码与表达式计算方法所隐含的舍入错误。[注:在被抽象的表达式中的中间操作被计算,就好到比无穷范围和精度,当最终操作被舍入到由表达式计算方法所确定的格式时。一个抽象的表达式也可能忽略对浮点异常的引发。]在<math.h>中的FP_CONTRACT编译指示提供了一种方法来禁用抽象的表达式。否则,表达式是否被抽象或如何被抽象是由实现定义的。[注:此许可目的专门为允许实现来利用可结合多个C操作的快速机器指令。由于抽象潜在地暗中破坏可预测性,并且甚至可能降低所包含表达式的精度,所以对它们的使用需要被良好定义,并且被清晰地文档化。]
6.5.11 按位异或操作符
6.5.12 按位或操作符
6.5.13 逻辑与操作符
6.5.14 逻辑或操作符