初始化派生结构的基部分/意外打包派生结构字段以对齐基结构的间隙

Initializing base part of the derived struct / Unexpected packing derived struct fields to alignment gap of the base struct

本文关键字:结构 派生 字段 间隙 对齐 基部 意外 包派生 初始化      更新时间:2023-10-16

意外地发现,clang++7 允许自己将派生结构(在下面的示例中为结构"B"和字段"B::u16_gap"(的字段紧密打包到基本结构(结构"A"(的对齐间隙。但是示例中的函数"zero_init"期望它的输入是具有未使用的对齐间隙的"A"对象,因此它可以被"memcpy"覆盖。当然,当应用于"B"对象时,此函数"zero_init"会覆盖"B::u16_gap"字段的任何值。

// compilation: clang++-7 -std=c++17 test.cpp
#include <cstdint>
#include <cstring>
#include <iostream>
struct A {
std::uint32_t u32 = 0;
std::uint16_t u16 = 0;
};
void zero_init(A& a) {
static constexpr A zero = {};
std::memcpy(&a, &zero, sizeof(A));
};
struct B: public A {
std::uint16_t u16_gap = 0;
};
static_assert(sizeof(A) == 8);
static_assert(sizeof(B) == 8); // clang++-7 packs additional field "B::u16_gap" to the alignment gap of the A (for g++-7 it is not true)
int main() {
B b;
A& a = b;
b.u16_gap = 123;
zero_init(a);
std::cout << b.u16_gap << std::endl; // writes "0" instead of expected "123"
}

此代码中格式错误的部分(偏离 C++17 标准(的"良好行为"规则(在哪里?

就像你的静态断言显示的那样,A 的大小是 8 个字节。 这意味着您的静态"零"A 对象由 8 个字节的零组成。memcpy不知道类型,因此您将纯 8 个字节复制到a的地址,从而覆盖存储在这 8 个字节中的u16_gap字段,如第二个断言所示。

如果你想改变这一点,你可以只复制6个字节,它应该可以工作。