C++标准中,结构体数组能否通过指针算术遍历对应数据成员?
结构体数组成员地址跳转的C++标准合法性问题
在类型为S的结构体数组中,相邻结构体的对应数据成员之间的字节偏移量确实等于sizeof(S)。那是否可以根据C++标准,通过将成员地址加上sizeof(S),直接从一个结构体的某个成员跳转到下一个结构体的对应成员?
示例代码如下:
#include <vector> #include <string> #include <iostream> using namespace std; struct S { vector<int> v; string s; int i; char c; }; void print_all(const string* s, size_t n, ptrdiff_t stride_in_bytes) { while (n > 0) { cout << *s << "\n"; --n; // 尝试从当前string成员跳转到下一个结构体的string成员 s = reinterpret_cast<const string*>(reinterpret_cast<uintptr_t>(s)+stride_in_bytes); } } int main(int, char**) { S my_array[3]; my_array[0].s = "a"; my_array[1].s = "b"; my_array[2].s = "c"; print_all(&my_array[0].s, 3, sizeof(S)); return 0; }
这段代码在GCC和MSVC中可以正常运行,但它是否符合C++标准?
结论:不符合C++标准,属于未定义行为
具体原因如下:
- 指针运算规则限制:C++标准规定,指针的算术运算仅允许在同一个数组的元素范围内进行(或指向数组末尾的位置)。这里的
string*指向的是各个独立S对象的成员,这些string本身并不构成一个数组。对这类指针进行偏移运算(哪怕通过uintptr_t转换后计算),已经违反了标准的指针操作规则。 - 解引用的合法性未被保证:即便通过强制类型转换得到了一个新的
string*,标准并不保证这个地址指向的是一个有效的、可访问的string对象。虽然实际编译器通常会按预期处理,但标准不对此行为负责,编译器有权进行任意优化,甚至生成错误代码。 - 结构体布局的隐含风险:虽然结构体数组的元素通常是紧凑排列的,相邻成员的偏移看起来确实是
sizeof(S),但标准允许编译器在特定场景下对结构体数组进行额外内存填充(尽管这种情况很少见),进一步增加了行为的不确定性。
实际编译器能运行这段代码,只是因为它们实现了超出标准要求的扩展行为,但这种代码不具备可移植性,也不能保证在所有编译器或优化级别下都正常工作。
内容的提问来源于stack exchange,提问作者umbert
相关产品推荐
相关产品推荐

