6.5.16 赋值操作符
语法
1、assignment-expression:
conditional-expression
unary-expression assignment-operator assignment-expression
assignment-operator: 以下之一
= *= /= %= += -= <<= >>= &= ^= |=
约束
2、一个赋值操作符应该具有一个可修改的左值作为其左操作数。
语义
3、一个赋值操作符将一个值存储在由左操作数所指派的对象中。一个赋值表达式在赋值后具有左操作数的值,[注:允许实现读此对象以确定该值,但不要求这么做,即使对象是用volatile限定的类型。]但并不是一个左值。赋值表达式的类型是左操作数的类型,在左值转换后即可具有。更新左操作数所存放的值的副作用顺序在左和右操作数的值计算之后。操作数的计算是没有顺序的。
6.5.16.1 简单赋值
约束
1、要遵守下列规则之一:[注:对于这些关于类型限定符的非对称的出现是由于将左值变为“表达式的值”并移除了任一类型限定符的转换(在6.3.2.1中指定)被应用于表达式的类型类别(比如,int volatile * const移除了const,但并不移除volatile)。]
——左操作数具有原子的、限定的、或非限定的算术类型,而右操作数具有算术类型;
——左操作数具有与右操作数相兼容的一个原子的、限定的、或非限定版本的一个结构体或联合体类型;
——左操作数具有原子的、限定的、或非限定的指针类型,且(考虑到左操作数在左值转换之后将会有的类型)两个操作数都具有指向限定或非限定版本的兼容类型的指针,而被左操作数所指向的类型具有被右值所指向的类型的所有限定符。
——左操作数具有原子的、限定的、或非限定的类型,且其中一个操作数是一个指向一个对象类型的指针,而另一个是一个指向一个限定或非限定版本的void的指针,而被左操作数所指向的类型具有被右操作数所指向类型的所有限定符。
——左操作数是一个原子的、限定的、或非限定的指针,而右操作数是一个空指针常量;或
——左操作数具有原子的、限定的、或非限定的_Bool,而右操作数是一个指针。
语义
2、在简单赋值(=)中,右操作数的值被转换为赋值表达式的类型,并替换由左操作数所指派的对象中的值。
3、如果正要存在一个对象中的值从另一个对象被读,两者以任一种形式叠交,那么该叠交应该是准确的,且两个对象应该具有一个兼容类型的限定或非限定版本;否则,行为是未定义的。
4、例1 在下列程序片段中
int f(void); char c; /* ... */ if( (c = f()) == -1 ) /* ... */
由函数所返回的int值可以被截断,当存储在char中时,然后在比较之前被转换回int宽度。在某一实现中,其“平凡的”char具有与unsigned char具有相同范围(并且char比int要窄),那么转换结果不能是负数,这样比较的操作数可能永远不会比较为相等的。从而,对于充分的可移植性,变量c应该被声明为int。
5、例2 在下列程序片段中:
char c; int i; long l; l = (c =i);
i的值被转换为赋值表达式c = i的类型,即char类型。在圆括号中括起来的表达式的值然后被转换为外部赋值表达式的类型,即long int类型。
6、例3 考虑以下程序片段
const char **cpp; char *p; const char c = ‘A‘; cpp = &p; // 违背了约束 *cpp = &c; // 有效 *p = 0; // 有效
第一个赋值是不安全的,因为它可能允许以下有效代码企图尝试改变const对象c的值。
6.5.16.2 复合赋值
约束
1、