通过显式算术计算结构体元素指针是否符合ISO C标准?
显式计算结构体元素指针的合法性与ISO C标准符合性
问题
显式通过算术计算结构体元素指针的方式是否合法?以下程序是否符合ISO C标准(而非仅在特定编译器上可运行)?
struct foo { int a, b, c; }; int f(struct foo *p) { // should return p->c char *q = ((char *)p) + 2 * sizeof(int); return *((int *)q); }
初步分析
该程序似乎符合部分指针合法使用规则:
- 加载的值与地址存储的类型一致;
- 计算得到的指针的provenance合法,由有效指针偏移得到且仍在原存储实例内;
- 结构体内部无类型混合,不会产生导致元素偏移不可预测的填充。
结论与标准依据
这个程序完全符合ISO C标准,具体依据如下:
结构体成员布局的确定性:
ISO C标准规定,结构体成员按声明顺序存储,且成员的偏移量必须满足自身类型的对齐要求。对于连续的同类型成员(如本例中的int a, b, c),由于int的大小等于其对齐要求(绝大多数平台均如此,且标准允许的实现也会保证这一点),成员之间不会存在填充。因此c相对于结构体起始地址的偏移量确实是2 * sizeof(int),显式计算的偏移是准确的。指针转换与算术的合法性:
标准允许将任意对象指针转换为字符类型指针(char*/unsigned char*/signed char*),且字符指针的算术运算可以用来遍历对象的字节。本例中从struct foo*转换为char*后进行偏移,得到的指针仍指向原结构体对象的内部,指针的来源(provenance)合法。指针转回与解引用的合法性:
将字符指针转回int*时,由于c的地址满足int类型的对齐要求(结构体起始地址对齐,偏移量是sizeof(int)的整数倍),转换后的指针是合法对齐的。后续解引用该指针时,访问的对象类型与指针类型一致,符合标准对指针解引用的要求。
注意事项
如果结构体成员类型不同,或成员之间因对齐要求存在填充,这种显式计算偏移的方式会导致未定义行为——但本例的结构体不存在这类问题,因此可以安全使用。
内容的提问来源于stack exchange,提问作者rwallace
相关产品推荐
相关产品推荐

