C++标准是否允许对标准布局结构体的同类型成员执行指针运算?
C++20标准下标准布局struct同类型连续成员的指针算术问题结论
你示例中对struct成员指针做算术运算的行为属于未定义行为,C++20标准没有任何条款保证&m.a + 1 == &m.b成立,即使MyStruct是标准布局类型。
规则依据
1. 指针算术的有效范围
C++20 [expr.add] 章节明确限定了指针算术的合法场景:
- 仅当指针指向某个数组的元素时,对该指针做加减整数的运算才具备明确定义,运算结果不能超出数组边界(含尾后一个位置)。
- 指向单个非数组对象的指针,视为指向长度为1的数组的第0位元素,仅允许做
+1运算得到合法的尾后指针,该尾后指针不可解引用,也不保证指向其他有效对象。
你给出的数组示例是完全合法的,因为所有指针操作都在int m[3]的数组范围内:
// Legit int m[3] = {0, 1, 2}; int const *p = m; std::cout << *p << *(p + 1) << *(p + 2) << std::endl;
但对struct成员的指针操作不符合上述要求:&m.a指向的是单个int成员,不属于任何数组,因此p+1只是合法的尾后指针,解引用*(p+1)的行为完全未定义,也不保证指向相邻的m.b成员。
2. 标准布局struct的布局约束
C++20 [class.prop] 章节对标准布局struct的成员布局仅做了最低限度的强制要求:
- 相同访问控制层级的非静态数据成员,按声明顺序地址递增排列,因此
&m.a < &m.b < &m.c必然成立,这也是你可以比较不同成员指针大小的依据。 - 首个非静态数据成员的地址与struct对象本身的地址相等。
- 标准允许编译器在任意两个成员之间、struct末尾插入任意字节的填充以满足对齐要求,即使相邻成员是完全相同的基础类型,也没有禁止插入填充的规定。
虽然绝大多数主流编译器在同类型基础成员之间不会插入填充,使得你给出的示例代码在实际运行中大概率能得到预期结果,但这属于编译器实现的特定行为,不是C++标准的强制要求,依赖这种行为的代码不具备可移植性,也不符合标准规范。
合法替代方案
如果需要连续访问多个同类型的属性,最安全的做法是直接将其定义为数组成员:
struct MyStruct { int vals[3]; }; static_assert(std::is_standard_layout_v<MyStruct>); MyStruct m = {0, 1, 2}; int const *p = m.vals; std::cout << *p << *(p + 1) << *(p + 2) << std::endl;
上述代码完全符合C++标准要求,具备100%的可移植性。
内容的提问来源于stack exchange,提问作者Adrian McCarthy
相关产品推荐
相关产品推荐

