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

标签 c++ inheritance alignment language-lawyer

出人意料地发现,clang++7 允许自己将派生结构的字段(在下面的示例中带有字段“B::u16_gap”的结构“B”)紧密打包到基本结构的对齐间隙(结构“A”)。但是示例中的函数“zero_init”期望它的输入是具有未使用对齐间隙的“A”对象,因此它可以被“mem​​cpy”覆盖。当应用于“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 的地址,从而覆盖 u16_gap 字段,即存储在这 8 个字节中,就像您的第二个断言显示的那样。

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

关于c++ - 初始化派生结构的基础部分/意外打包派生结构字段到基础结构的对齐间隙,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52688933/

相关文章:

c++ - 您可以将链接器/附加依赖项等复制到其他项目吗

c++ - 将 ISO C++ 类公开给 VB6 应用程序

c++ - 当我的程序崩溃时如何自动生成堆栈跟踪

C、结构指针多态

html - 具有两个跨度的列表项左右对齐

css - 如何更改 <div> 标签中图像的相对位置

c++ - 无法将创建的 .so 文件链接到 main.cpp

class - puppet 节点类包含中继承的公认替代方案吗?

java - java中如何判断一个对象外部扩展了什么

html - 将 li 直线向下对齐