【发布时间】:2019-03-12 08:06:06
【问题描述】:
意外地发现,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 标准的“良好行为”规则)在哪里?
【问题讨论】:
-
你为什么期望 123?之后你将它归零。
-
因为“zero_init”应该只触及“B”对象的“A”部分。它合法地(乍一看)只写入“A”基结构区域。
-
建议您添加 [language-lawyer] 标签,因为这看起来很奇怪。您的测试表明结构的
A和B部分将具有相同的地址,我认为这是不正确的标准
标签: c++ inheritance alignment language-lawyer