ISO/IEC 9899:2011 条款6.3.2——其它操作数
6.3.2 其它操作数
6.3.2.1 左值,数组,与函数指派符
1、一个左值是潜在地指派一个对象的一个表达式(具有一个对象类型,而不是void);[注:名字“左值”源自于赋值表达式E1 = E2,在此表达式中E1要求为一个(可修改的)左值。如果认作为表示一个对象“定位器值”可能更好。在此国际标准中描述为“一个表达式的值”有时也称为“右值”。一个左值的一个明显例子是一个对象的一个标识符。作为更进一步的例子,如果E是一个单目表达式,它是一个指向一个对象的指针,那么*E是一个指派E所指向对象的一个左值。]如果一个左值当它被计算时并不指派一个对象,那么行为是未定义的。当一个对象被称为具有一个特定类型时,该类型由用于指派该对象的左值指定。一个可修改的左值是一个不具有数组类型、不具有一个不完整类型、不具有一个const限定类型的左值,并且如果该左值是一个结构体或联合体,那么它不具有带有一个const限定类型的任一成员(递归地包括所有所包含的聚合或联合体的任一成员或元素)。
2、除了当一个左值是sizeof操作符、_Alignof操作符、单目&操作符、++操作符、--操作符的操作数的操作数时,或者是.操作符或赋值操作符的左操作数时,一个不具有数组类型的左值被转换为被指派对象中所存储的值(并且不再是一个左值);这被称为左值转换。如果左值具有限定类型,那么转换后的值具有该左值类型的非限定版本;此外,如果左值具有原子类型,那么转换后的值具有该左值类型的非原子版本;否则,该值具有左值类型。如果左值具有一个不完整类型并且不具有数组类型,那么行为是未定义的。如果左值指派了自动存储周期的一个对象,该对象可能用register存储类说明符来声明(从不取其地址),并且该对象未被初始化(没有用一个初始化器来声明并且在使用之前没有对它赋值),那么行为是未定义的。
3、除了当一个表达式是作为sizeof操作符、_Alignof操作符或单目&操作符的操作数时,或用于初始化一个数组的一个字符串字面量时,具有“类型的数组”类型的一个表达式被转换为具有“指向类型的指针”类型的一个表达式,该表达式指向数组对象的首个元素,并且不是一个左值。如果该数组对象具有寄存器存储类,那么行为是未定义的。
4、一个函数指派符是一个具有函数类型的表达式。除了当它是sizeof操作符、_Alignof操作符[注:因为这个转换不会发生,所以sizeof或_Alignof操作符的操作数仍然是一个函数指派符并违反了6.5.3.4的约束。],或单目&操作符的操作数时,一个具有类型“函数返回类型”的函数指派符被转换为一个表达式,该表达式具有“指向函数返回类型的指针”类型。[译者注:在C语言中,一个函数指派符为一个函数的标识符,用于表明一个函数类型。这段话中表明了,一个函数标识符在它不作为sizeof或_Alignof的操作数时,被转换为一个函数指针类型。如一下代码:
void FunA(void) { } int main(void) { size_t size = sizeof(FunA); // FunA为函数类型,非法 size = _Alignof(FunA); // FunA为函数类型,非法 size = sizeof(&FunA); // (&FunA)为函数指针类型,合法 size = _Alignof(&FunA); // (&FunA)为函数指针类型,合法 void (*pFunc)(void) = FunA; // 这里的FunA具有函数指针类型 }
上述代码中,main函数里出现的FunA均为函数指派符。另外,现在大部分C语言编译器对于sizeof操作符以及_Alignof操作符里面所包含的函数指派符,仍然作为函数指针类型,尽管这并非严格遵从C语言标准。
]
6.3.2.2 void
1、一个void表达式(具有void类型的一个表达式)的(不存在的)值不应该以任一种方法被使用,并且隐式的或显式的转换(除了对void的转换)不应该被应用到这样的一个表达式上。如果任一其它类型的一个表达式被计算出为一个void表达式,那么其值或指派符被丢弃。(一个void表达式为其副作用而被计算。)
6.3.2.3 指针
1、一个指向void的指针可以被转换为指向任一对象类型的指针;也能从指向任一对象类型的指针转换为指向void的指针。一个指向任一对象类型的指针可以被转换回为一个指向void的指针;结果与原始指针相比较应该相等。
2、对于任一限定符q,一个指向非q限定的类型可以被转换为一个指向该类型的q限定版本;在原始对象中所存储的值与转换后的指针所指向对象的值比较应该相等。
3、一个带有0值的整数常量表达式,或被投射到类型void*的这么一个表达式,被称为一个空指针常量。[注:NULL宏被定义在<stddef.h>中(以及在其它头文件中)作为一个空指针常量;见7.19。]如果一个空指针常量被转换为一个指针类型,那么结果指针被称为一个空指针,空指针与一个指向任一对象或函数的指针比较必定不相等。
4、一个空指针到另一个指针类型的转换产生一个那个类型的空指针。任何两个空指针比较应该相等。
5、一个整数可以被转换为任一指针类型。除了作为先前所指定的,该结果是由实现定义的,可能不会被正确对齐,可能不指向一个被引用类型的实体,并且可能是一个陷阱表示。[注:对于将一个指针转换为一个整数或将一个整数转换为一个指针的映射函数,目的在于与执行环境的寻址结构相一致。]
6、任一指针类型可以被转换为一个整数类型。除了作为先前所指定的,该结果是实现定义的。如果结果不能用那个整数类型来表示,那么行为是未定义的。结果不需要在任一整数类型的范围之内。
7、一个指向一个对象类型的一个指针可以被转换为指向一个不同对象类型的一个指针。如果该结果指针没有对那个被引用类型正确地对齐[注:一般来说,“正确地对齐”的概念是可传递的:如果一个类型A的指针被正确对齐到一个类型B的指针,而类型B的指针正确对齐到类型C的指针,那么一个指向类型A的指针也被正确地对齐到类型C的指针。],那么行为是未定义的。否则,当再次转换回时,结果应该与原始指针比较相等。当一个指向对象的指针被转换为指向一个字符类型指针时,结果指向该对象的最低编址的字节。结果的相继递增,到达对象的尺寸,产生对该对象剩余字节字节的指针。
8、一个指向一个类型的函数的指针可以被转换为指向一个另一类型的函数的指针,并且再转换回来;结果与原始指针比较应该相等。如果一个被转换的指针被用于调用一个函数,其类型与被引用类型不兼容,那么行为是未定义的。