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

时间:2018-10-07 13:24:07

标签: c++ inheritance alignment language-lawyer

意外地发现,clang ++ 7允许其自身将派生结构的字段(以下示例中的字段“ B :: u16_gap”的结构“ B”与字段“ B”紧紧打包)到基本结构的对齐间隙(结构“ 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标准的“良好行为”规则的背离)?

1 个答案:

答案 0 :(得分:0)

就像您的静态断言所示,A的大小为8个字节。 这意味着您的静态“零”对象包含8个字节的零。 memcpy不了解类型,因此您将普通的8个字节复制到a的地址,然后覆盖u16_gap字段,该字段存储在这8个字节中,就像您的第二个断言显示。

如果您要更改此设置,则只需复制6个字节即可。