今天早上我和一位同事讨论了关于检测字节序的“编码技巧”的正确性。
诀窍是:
bool is_big_endian()
{
union
{
int i;
char c[sizeof(int)];
} foo;
foo.i = 1;
return (foo.c[0] == 1);
}
对我来说,似乎union
的这种用法是不正确的,因为设置联合的一个成员并读取另一个成员不定义良好。但我不得不承认,这只是一种感觉,我缺乏实际的证据来强化我的观点。
这个技巧是否正确?谁在这里?
答案 0 :(得分:11)
您的代码不可移植。它可能适用于某些编译器,也可能不适用。
当你试图访问union的非活动成员时,你是正确的行为是正确的[就像给出的代码一样]
$ 9.5 / 1
在联盟中,最多其中一个数据成员可以随时处于活动状态,也就是说,最多一个数据成员的值可以随时存储在一个联合中
因此foo.c[0] == 1
不正确,因为此时c
未处于活动状态。如果你认为我错了,请随意纠正我。
答案 1 :(得分:5)
不要这样做,更好地使用以下内容:
#include <arpa/inet.h>
//#include <winsock2.h> // <-- for Windows use this instead
#include <stdint.h>
bool is_big_endian() {
uint32_t i = 1;
return i == htonl(i);
}
说明:
htonl函数将u_long从主机转换为TCP / IP网络字节顺序(这是big-endian)。
参考文献:
答案 2 :(得分:2)
你认为该代码没有明确定义的行为是正确的。以下是如何移植:
#include <cstring>
bool is_big_endian()
{
static unsigned const i = 1u;
char c[sizeof(unsigned)] = { };
std::memcpy(c, &i, sizeof(c));
return !c[0];
}
// or, alternatively
bool is_big_endian()
{
static unsigned const i = 1u;
return !*static_cast<char const*>(static_cast<void const*>(&i));
}
答案 3 :(得分:0)
该函数应命名为is_little_endian。我想你可以使用这个联合技巧。或者也可以演员表演。
答案 4 :(得分:0)
代码有未定义的行为,尽管有些(大多数?)编译器会 定义它,至少在有限的情况下。
标准的意图是reinterpret_cast
用于
这个。然而,自标准以来,这种意图并未得到很好的表达
无法真正定义行为;当时没有定义它的愿望
硬件不支持它(例如由于对齐问题)。和
很明显,你不能只在{两个}之间reinterpret_cast
任意类型,并希望它工作。
从实施质量的角度来看,我希望两者都有
union
技巧和reinterpret_cast
工作,如果 union
或
reinterpret_cast
位于同一功能块中; union
应该
只要编译器可以看到最终类型是union
(虽然我使用的编译器并非如此)。