意外地发现,clang++7允许自己将派生结构(下面示例中的结构"B“与字段"B::u16_gap”)的字段紧密打包到基本结构的对齐间隙(结构"A")。但是示例中的函数"zero_init“期望它的输入是"A"-object,它没有使用对齐间隙,因此它可以被"memcpy”覆盖。当然,这个函数"zero_init“覆盖"B::u16_gap"-field的任何值,当应用于"B"-object时。
// 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标准的“良好行为”规则)在哪里?
发布于 2018-10-07 13:39:24
就像静态断言显示的那样,A的大小是8个字节。这意味着静态“零”A-对象由8个字节的零组成。memcpy不知道类型,所以您将普通的8个字节复制到a的地址,从而覆盖u16_gap字段,该字段存储在这8个字节中,如第二个assert所示。
如果你想改变它,你只需复制6个字节,它就能工作。
https://stackoverflow.com/questions/52688933
复制相似问题