char 数组的严格别名和并集
Strict aliasing and union of char arrays
我有点期待答案已经是什么了,但很好奇标准对此有何规定。
设置:我想控制结构中字段的确切偏移量,并直接在字段类型中指定它们。这是魔术:
#include <type_traits>
#include <cstdint>
#include <cstring>
#include <new>
template <uint32_t OFFSET, typename T>
struct FieldOverlay
{
static_assert(std::is_trivial<T>::value, "Can only be used with trivial types");
FieldOverlay() = delete;
FieldOverlay(FieldOverlay&& other) = delete;
FieldOverlay(FieldOverlay const& other) = delete;
FieldOverlay& operator = (T const& val) { new (buf + OFFSET) T(val); return *this; }
operator T()
{
T v;
::memcpy(&v, buf + OFFSET, sizeof(T));
return v;
}
private:
char buf[OFFSET + sizeof(T)];
};
// Precisely control member offsets
union MyMessage
{
FieldOverlay<0, uint32_t> x;
FieldOverlay<7, uint32_t> y;
};
void exampleUsage(MyMessage& m)
{
m.y = m.x;
}
struct MyMessageEquivalent
{
uint32_t x;
char padding[3];
uint32_t y;
} __attribute__ ((packed));
这在 gcc 6.3 上编译 -O3 -std=c++1z -fstrict-aliasing -Wall -Wpedantic -Wextra -Werror
没有任何错误,并且按预期工作。 (见神螺栓:https://godbolt.org/g/DHWLD9)
问:这个符合犹太洁食标准吗?我认为它变得非常冒险,因为工会 MyMessage
中没有一个 "active" 成员。但是,由于所有内容都是通过 char
数组访问的,这对严格别名规则有帮助吗?
您的代码有很多问题,但您会在严格别名成为问题之前调用 UB long。
MyMessage::x
和 MyMessage::y
不是布局兼容类型。它们也没有共同的初始序列。是的,尽管它们都存储 char
数组,但它们不存储 相同大小的 数组 char
。这两个数组的长度不同,并且在公共初始序列规则中没有任何内容表明包含两个具有相同基本类型但大小不同的数组的结构具有公共初始序列。
因此,当 y
是联盟的活跃成员时,您不能尝试访问 x
。反之亦然。
仅供参考:您重新解释的演员阵容也引起了 UB。那个内存里没有T
,reinterpret_cast
不能创建对象。因此访问该内存就好像它包含 T
违反了标准。此外,该标准不允许您通过未对齐的指针访问对象。
所以基本上,您尝试做的事情永远不会奏效。
我有点期待答案已经是什么了,但很好奇标准对此有何规定。
设置:我想控制结构中字段的确切偏移量,并直接在字段类型中指定它们。这是魔术:
#include <type_traits>
#include <cstdint>
#include <cstring>
#include <new>
template <uint32_t OFFSET, typename T>
struct FieldOverlay
{
static_assert(std::is_trivial<T>::value, "Can only be used with trivial types");
FieldOverlay() = delete;
FieldOverlay(FieldOverlay&& other) = delete;
FieldOverlay(FieldOverlay const& other) = delete;
FieldOverlay& operator = (T const& val) { new (buf + OFFSET) T(val); return *this; }
operator T()
{
T v;
::memcpy(&v, buf + OFFSET, sizeof(T));
return v;
}
private:
char buf[OFFSET + sizeof(T)];
};
// Precisely control member offsets
union MyMessage
{
FieldOverlay<0, uint32_t> x;
FieldOverlay<7, uint32_t> y;
};
void exampleUsage(MyMessage& m)
{
m.y = m.x;
}
struct MyMessageEquivalent
{
uint32_t x;
char padding[3];
uint32_t y;
} __attribute__ ((packed));
这在 gcc 6.3 上编译 -O3 -std=c++1z -fstrict-aliasing -Wall -Wpedantic -Wextra -Werror
没有任何错误,并且按预期工作。 (见神螺栓:https://godbolt.org/g/DHWLD9)
问:这个符合犹太洁食标准吗?我认为它变得非常冒险,因为工会 MyMessage
中没有一个 "active" 成员。但是,由于所有内容都是通过 char
数组访问的,这对严格别名规则有帮助吗?
您的代码有很多问题,但您会在严格别名成为问题之前调用 UB long。
MyMessage::x
和 MyMessage::y
不是布局兼容类型。它们也没有共同的初始序列。是的,尽管它们都存储 char
数组,但它们不存储 相同大小的 数组 char
。这两个数组的长度不同,并且在公共初始序列规则中没有任何内容表明包含两个具有相同基本类型但大小不同的数组的结构具有公共初始序列。
因此,当 y
是联盟的活跃成员时,您不能尝试访问 x
。反之亦然。
仅供参考:您重新解释的演员阵容也引起了 UB。那个内存里没有T
,reinterpret_cast
不能创建对象。因此访问该内存就好像它包含 T
违反了标准。此外,该标准不允许您通过未对齐的指针访问对象。
所以基本上,您尝试做的事情永远不会奏效。