初始化派生结构的基部分/意外打包派生结构字段以对齐基结构的间隙
Initializing base part of the derived struct / Unexpected packing derived struct fields to alignment gap of the base struct
意外地发现,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个字节,它应该可以工作。
相关文章:
- 初始化派生结构的基部分/意外打包派生结构字段以对齐基结构的间隙
- 有没有办法将C++结构/类中的所有定义都纳入范围而不从中派生?
- 受保护的嵌套结构不能用作派生外部类中的返回类型?
- 基类类型向量中的派生结构
- 使用 static_cast 更改派生结构成员
- C++ 在派生类中访问基的私有结构
- 为什么C++可以使用派生结构来实例化其父模板结构,而父模板可以调用子结构的函数?
- C 结构实现派生接口
- 从派生类重新定义基类中定义的结构
- 具有灵活大小的结构的 MPI 派生数据类型
- 是否可以将基类的结构分配给派生类的结构
- 如何在派生类中初始化继承的模板POD结构
- 是否可以在 c++ 中的派生类中以特定偏移量附加结构
- 编译器不应该对派生类中隐藏的基本结构的成员变量发出警告吗?
- 如何使派生类定义数据结构
- 如何在MPI中从结构类型创建新的派生数据类型
- 从具有静态结构的基类派生
- 指向类层次结构的派生部分的指针
- 类层次结构设计,避免从基类到派生类的向下转换
- 不能交换从 Vector 派生的类中的结构