表达式(五)
3、结构和联合成员
结构和联合成员具有以下两种语法格式:
第一种语法格式:
postfix-expression . identifier
其中postfix-expression是后缀表达式,其类型为原子的、限定的或者非限定的结构或者联合类型;identifier是表示结构或者联合命名成员的标识符,其值是整个表达式的值。如果后缀表达式是左值,则整个表达式是左值。如果后缀表达式具有限定类型,则整个表达式具有成员类型的限定版本。
struct s{ int i; double d; }; ... const struct s cs = {5, 3.14};
cs.i和cs.d值分别为5、3.14;类型分别为const int、const double类型。
第二种语法格式:
postfix-expression -> identifier
其中postfix-expression是后缀表达式,其类型为指向原子的、限定的或者非限定的结构或者联合类型的指针;identifier是表示结构或者联合命名成员的标识符,其值是整个表达式的值。整个表达式是左值。如果后缀表达式是指向限定类型的指针,则整个表达式具有成员类型的限定版本。
struct s{ int i; double d; }; ... const struct s cs = {5, 3.14};
(&cs)->i和(&cs)->d值分别为5、3.14;类型分别为const int、const double类型。
在&cs是有效指针表达式的情况下,表达式(&cs)->i与表达式cs.i是相同表达式。如果函数返回类型是结构类型或者联合类型,cs是函数返回值,这种情况下&cs不是有效指针表达式。
读取联合成员值时,如果最后存储值不是该成员值,值对象表示的适当部分将重新解释为新类型的对象表示,这一过程称为类型双关(type punning)。结果可能是非值表示(non-value representation)。
union u{ int i; float f; }; ... union u data = {.f = 3.14f}; printf("%f\n", data.f); //输出3.140000。 printf("%d\n", data.i); //输出垃圾值1078523331。
访问原子结构或者原子联合的成员会导致未定义行为,例如:如果一个线程对结构或者联合的访问与另一个线程对其某个成员的访问发生冲突(其中至少一个访问是修改。),就会导致数据竞争(data race)。可以将非原子对象赋值给原子对象或者将原子对象赋值给非原子对象的方式安全地访问其成员。
struct s{ int i; float f; }; ... _Atomic struct s as = {5,3.14f}; struct s ss; as.i = 10; //非法。 ss.i = as.i; //非法。 ss = as; //合法。 as = (struct s){10,1.23f}; //合法。 as = ss; //合法。
对于一个或者多个初始成员序列,如果相应成员具有兼容类型(对于位字段,具有相同宽度。),则两个结构共享共同初始序列。为简化联合使用,ISO/IEC 9899:2024标准规定:如果一个联合包含几个共享共同初始序列(common initial sequence)的结构,并且如果联合当前包含其中一个结构,那么在任何完整联合类型声明可见的地方,都允许检查其中任何结构的共同初始部分。
struct a{ int i; double d; }; struct b{ int i; int * ptr; }; union u{ struct a sa; struct b sb; }; ... union u uu = {.sa={5, 3.14}}; printf("%d\n", uu.sb.i); //输出5。
上述代码中uu.sa.i和uu.sb.i是共同初始序列。
如果函数func返回类型是结构类型或者联合类型,x是该结构类型或者联合类型的成员,则表达式func().x是有效的后缀表达式,但不是左值;如果函数func返回指向结构或者联合的指针,x是该结构或者联合的成员,则表达式func()->x是有效的后缀表达式,也是左值。
struct s{ int si; double sd; }; struct s func(int fi, double fd, struct s ss) { ss.si = fi; ss.sd = fd; return ss; } struct s * f(int fi, double fd, struct s * ptr) { ptr->si = fi; ptr->sd = fd; return ptr; } ... struct s data; int i; i = func(10, 0.0, data).si; //合法。 func(10, 0.0, data).si = 20; //非法,func(10, 0.0, data).si不是左值。 i = f(15, 0.0, &data)->si; //合法。 f(15, 0.0, &data)->si = 25; //合法。