例如,
#include <cstdint>
#include <cstdio>
struct ipv4addr {
union {
std::uint32_t value;
std::uint8_t parts[4];
};
};
int main() {
ipv4addr addr;
addr.value = static_cast<std::uint32_t>(-1);
std::printf("%hhu.%hhu.%hhu.%hhu",
addr.parts[0], addr.parts[1], addr.parts[2], addr.parts[3]);
}
每cppref,
该分配的详细信息是实现定义的, 从工会成员那里读取的不确定行为 最近写的。
因此,看起来代码调用了未定义的行为。但是页面上还显示
如果两个联合成员是标准布局类型,则可以明确定义为 在任何编译器上检查它们的公共子序列。
我不太明白这一点。是否使代码行为定义明确?
还要注意cppref在type aliasing上的描述。
每当尝试读取或修改存储的值时, 类型为
DynamicType
的对象通过类型为AliasedType
的glvalue, 除非满足以下条件之一,否则行为是不确定的:
- [...]
AliasedType
是std::byte
,char
或unsigned char
:这允许将任何对象的对象表示形式检查为 字节。
我想这也适用于std::uint8_t
。不?
答案 0 :(得分:2)
常见的初始子序列有一个荒谬的特定定义。 int
和struct foo{int x;}
不具有共同的初始子序列。
struct foo{int x;}
和struct bar{int y;}
具有共同的初始子序列。
通过无关类型读取内存与从并集替代读取不同。该文本在那里没有任何作用。
您可以执行(std::unit8_t const*)&addr.value
并将其视为4字节数组,前提是您的平台具有unit8_t
。您获得的字节值是实现定义的。
但是,根据标准,您不能从parts[i]
中读取(当值存在时)。
当标准声明未在标准下定义行为时,编译器可以自由指定行为,除非在编译期constexpr
评估期间。
答案 1 :(得分:0)
读取最近未分配的工会成员的内容确实是未定义的行为。但是,我使用过的大多数编译器都具有非标准扩展名来允许它。
从技术上讲,如果您希望此操作安全无虞,则应将ip存储为int32_t
,并通过reinterpret_cast
重新解释以读取单个字节,如下所示:
int32_t ip = 185734;
int8_t *ip_bytes = reinterpret_cast<int8_t*>(&ip).
ip_bytes[etc]...
但是,您应该记住平台的字节顺序会影响任何32位读/写的字节顺序。因此,完全废弃int32_t
的想法可能更安全,而只需使用字节数组即可。这完全取决于您的需求和/或您可能正在使用的任何库。
答案 2 :(得分:-2)
定义明确,可以完全执行fwrite,然后执行四个get调用以将其读回。实际结果取决于机器。在CPU偏爱的任何存储模式下,“获取此uint32的字节”。