通过指向首个成员的指针访问未知大小类数组成员是否合法?
问题解答
一、当前实现的合法性
你的代码实现在符合C++标准的前提下是合法的,核心依据来自C++标准对标准布局类型(standard-layout type)的规则:
node_base和所有node<n>都是标准布局类型:它们没有虚函数、没有不同访问控制的成员,也没有非标准布局的基类/成员。- 对于标准布局类型,如果一个结构体的第一个非静态数据成员是另一个标准布局类型,那么整个对象的地址与该成员的地址完全相同(即
&a.base == &a)。 - 你通过
static_assert(offsetof(node, data) == sizeof(node_base))强制保证了node_base和data之间没有填充字节,确保从head指针偏移sizeof(node_base)后正好指向data数组的起始位置。 - 将对象指针转换为
char*并访问其存储字节是C++标准允许的操作(用于访问对象的底层表示),而data本身就是char数组,直接访问这些字节等价于访问数组元素。
不过需要注意:这种合法性依赖于上述前提,如果后续修改代码破坏了node<n>的标准布局(比如添加虚函数、调整成员访问控制),或者移除了static_assert导致出现填充字节,代码就会变成未定义行为。
二、不增大sizeof(node<n>)的规范实现
如果想要更符合C++类型系统的规范写法,同时不增加node<n>的大小,可以利用标准布局类型的指针转换规则优化代码,避免多层reinterpret_cast的嵌套:
void process_queue(node_base* head) { while (head) { // 利用标准布局类型首成员地址与对象地址相同的规则,直接获取整个node对象的字节指针 char* node_bytes = static_cast<char*>(static_cast<void*>(head)); // 偏移到data数组起始位置 char* data = node_bytes + sizeof(node_base); for (int i = 0; i < head->size; ++i) { data[i] = i; } head = head->next; } }
这种写法逻辑更清晰,本质和你的原代码等价,但更符合标准中对指针转换的推荐用法,同时依然保持node<n>的大小不变——因为没有添加任何额外成员或虚函数,完全依赖标准布局类型的固有特性。
内容的提问来源于stack exchange,提问作者Lukas Lang
相关产品推荐
相关产品推荐

