6.5.17 赋值运算符
TIP
赋值这一节要分开看:简单赋值和复合赋值并不只是“写法短一点”的区别,标准对其约束和单次求值语义都写得很细。
6.5.17.1 一般规定
赋值表达式包括简单赋值与复合赋值。
赋值运算符左操作数必须是一个可修改左值。
赋值表达式的结果不是左值,其类型是赋值表达式左操作数的类型。
6.5.17.2 简单赋值
- 简单赋值形如:
c
E1 = E21
右操作数的值会转换为适合赋给左操作数对象的类型,然后存入左操作数所指代的对象。
允许的类型组合包括:
- 算术类型赋给算术类型;
- 兼容结构体或联合体类型之间赋值;
- 指针与兼容指针、
void *、空指针常量、nullptr_t等之间赋值; bool、原子类型等遵循其各自规则。
赋值运算符保存的是左操作数对象中的新值;表达式结果具有左操作数类型,但不是左值。
赋值的副作用在左、右操作数值计算之后发生。
WARNING
像 const char **cpp; char *p; cpp = &p; 这种赋值违反约束,不是“只是编译器挑剔”,而是标准明确不安全。
6.5.17.3 复合赋值
- 复合赋值包括:
text
+= -= *= /= %= <<= >>= &= ^= |=1
对
+=和-=:- 左操作数可以是指向完整对象类型的指针,右操作数是整数类型;
- 或左操作数与右操作数都具有算术类型。
对其余复合赋值,左操作数必须具有算术类型,且两个操作数类型必须满足对应二元运算符本来的要求。
若任一操作数是十进制浮点类型,则另一操作数不得是标准浮点类型、复数类型或虚数类型。
E1 op= E2等价于:
c
E1 = E1 op (E2)1
但有一个关键区别:E1 只会被求值一次。
对于与不定序函数调用的关系,整个复合赋值算作一次求值。
若
E1是原子类型,则复合赋值是一个带memory_order_seq_cst语义的读改写操作。
IMPORTANT
a[i] += f() 不能简单理解成文本替换成 a[i] = a[i] + f()。标准只保证语义等价,同时保证左边那一份 a[i] 只求值一次。