Linux C++ 中的 64 位 ntohl()?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/809902/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
64 bit ntohl() in C++?
提问by Tom
The man pages for htonl()
seem to suggest that you can only use it for up to 32 bit values. (In reality, ntohl()
is defined for unsigned long, which on my platform is 32 bits. I suppose if the unsigned long were 8 bytes, it would work for 64 bit ints).
的手册页htonl()
似乎建议您最多只能将它用于 32 位值。(实际上,ntohl()
是为 unsigned long 定义的,在我的平台上是 32 位。我想如果 unsigned long 是 8 个字节,它适用于 64 位整数)。
My problem is that I need to convert 64 bit integers (in my case, this is an unsigned long long) from big endian to little endian. Right now, I need to do that specific conversion. But it would be even nicer if the function (like ntohl()
) would NOT convert my 64 bit value if the target platform WAS big endian. (I'd rather avoid adding my own preprocessor magic to do this).
我的问题是我需要将 64 位整数(在我的例子中,这是一个 unsigned long long)从 big endian 转换为 little endian。现在,我需要进行特定的转换。但是,ntohl()
如果目标平台是 big endian,那么如果函数(如)不会转换我的 64 位值,那就更好了。(我宁愿避免添加我自己的预处理器魔法来做到这一点)。
What can I use? I would like something that is standard if it exists, but I am open to implementation suggestions. I have seen this type of conversion done in the past using unions. I suppose I could have a union with an unsigned long long and a char[8]. Then swap the bytes around accordingly. (Obviously would break on platforms that were big endian).
我可以使用什么?我想要一些标准的东西,如果它存在的话,但我愿意接受实施建议。我曾经看到过使用联合完成的这种类型的转换。我想我可以有一个带有 unsigned long long 和 char[8] 的联合。然后相应地交换字节。(显然会在大端平台上中断)。
回答by paxdiablo
To detect your endian-ness, use the following union:
要检测您的字节顺序,请使用以下联合:
union {
unsigned long long ull;
char c[8];
} x;
x.ull = 0x0123456789abcdef; // may need special suffix for ULL.
Then you can check the contents of x.c[]
to detect where each byte went.
然后您可以检查 的内容x.c[]
以检测每个字节的去向。
To do the conversion, I would use that detection code once to see what endian-ness the platform is using, then write my own function to do the swaps.
要进行转换,我将使用该检测代码一次以查看平台使用的字节序,然后编写自己的函数来进行交换。
You could make it dynamic so that the code will run on any platform (detect once then use a switch inside your conversion code to choose the right conversion) but, if you're only going to be using one platform, I'd just do the detection once in a separate program then code up a simple conversion routine, making sure you document that it only runs (or has been tested) on that platform.
您可以使其动态化,以便代码可以在任何平台上运行(检测一次,然后在您的转换代码中使用一个开关来选择正确的转换)但是,如果您只打算使用一个平台,我会这样做在单独的程序中检测一次,然后编写一个简单的转换例程,确保您记录它仅在该平台上运行(或已测试)。
Here's some sample code I whipped up to illustrate it. It's been tested though not in a thorough manner, but should be enough to get you started.
这是我为说明它而编写的一些示例代码。虽然没有经过全面测试,但它已经过测试,但应该足以让您入门。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TYP_INIT 0
#define TYP_SMLE 1
#define TYP_BIGE 2
static unsigned long long cvt(unsigned long long src) {
static int typ = TYP_INIT;
unsigned char c;
union {
unsigned long long ull;
unsigned char c[8];
} x;
if (typ == TYP_INIT) {
x.ull = 0x01;
typ = (x.c[7] == 0x01) ? TYP_BIGE : TYP_SMLE;
}
if (typ == TYP_SMLE)
return src;
x.ull = src;
c = x.c[0]; x.c[0] = x.c[7]; x.c[7] = c;
c = x.c[1]; x.c[1] = x.c[6]; x.c[6] = c;
c = x.c[2]; x.c[2] = x.c[5]; x.c[5] = c;
c = x.c[3]; x.c[3] = x.c[4]; x.c[4] = c;
return x.ull;
}
int main (void) {
unsigned long long ull = 1;
ull = cvt (ull);
printf ("%llu\n",ull);
return 0;
}
Keep in mind that this just checks for pure big/little endian. If you have some weird variant where the bytes are stored in, for example, {5,2,3,1,0,7,6,4} order, cvt()
will be a tad more complex. Such an architecture doesn't deserve to exist, but I'm not discounting the lunacy of our friends in the microprocessor industry :-)
请记住,这只是检查纯大/小端。如果您有一些奇怪的变体,其中字节存储在,例如 {5,2,3,1,0,7,6,4} 顺序,cvt()
会有点复杂。这样的架构不应该存在,但我并没有贬低我们微处理器行业朋友的疯狂:-)
Also keep in mind that this is technically undefined behaviour, as you're not supposed to access a union member by any field other than the last one written. It will probably work with most implementations but, for the purist point of view, you should probably just bite the bullet and use macros to define your own routines, something like:
还要记住,这在技术上是未定义的行为,因为除了最后一个写入的字段之外,您不应该通过任何字段访问联合成员。它可能适用于大多数实现,但是,就纯粹主义者的观点而言,您可能应该硬着头皮使用宏来定义自己的例程,例如:
// Assumes 64-bit unsigned long long.
unsigned long long switchOrderFn (unsigned long long in) {
in = (in && 0xff00000000000000ULL) >> 56
| (in && 0x00ff000000000000ULL) >> 40
| (in && 0x0000ff0000000000ULL) >> 24
| (in && 0x000000ff00000000ULL) >> 8
| (in && 0x00000000ff000000ULL) << 8
| (in && 0x0000000000ff0000ULL) << 24
| (in && 0x000000000000ff00ULL) << 40
| (in && 0x00000000000000ffULL) << 56;
return in;
}
#ifdef ULONG_IS_NET_ORDER
#define switchOrder(n) (n)
#else
#define switchOrder(n) switchOrderFn(n)
#endif
回答by Francis
some BSD systems has betoh64
which does what you need.
一些 BSD 系统可以满足betoh64
您的需求。
回答by Snazzer
I like the union answer, pretty neat. Typically I just bit shift to convert between little and big endian, although I think the union solution has fewer assignments and may be faster:
我喜欢工会的回答,非常简洁。通常我只是在小端和大端之间转换,尽管我认为联合解决方案的分配更少并且可能更快:
//note UINT64_C_LITERAL is a macro that appends the correct prefix
//for the literal on that platform
inline void endianFlip(unsigned long long& Value)
{
Value=
((Value & UINT64_C_LITERAL(0x00000000000000FF)) << 56) |
((Value & UINT64_C_LITERAL(0x000000000000FF00)) << 40) |
((Value & UINT64_C_LITERAL(0x0000000000FF0000)) << 24) |
((Value & UINT64_C_LITERAL(0x00000000FF000000)) << 8) |
((Value & UINT64_C_LITERAL(0x000000FF00000000)) >> 8) |
((Value & UINT64_C_LITERAL(0x0000FF0000000000)) >> 24) |
((Value & UINT64_C_LITERAL(0x00FF000000000000)) >> 40) |
((Value & UINT64_C_LITERAL(0xFF00000000000000)) >> 56);
}
Then to detect if you even need to do your flip without macro magic, you can do a similiar thing as Pax, where when a short is assigned to 0x0001 it will be 0x0100 on the opposite endian system.
然后要检测您是否甚至需要在没有宏魔法的情况下进行翻转,您可以执行与 Pax 类似的操作,其中当将短路分配给 0x0001 时,在相反的字节序系统上它将是 0x0100。
So:
所以:
unsigned long long numberToSystemEndian
(
unsigned long long In,
unsigned short SourceEndian
)
{
if (SourceEndian != 1)
{
//from an opposite endian system
endianFlip(In);
}
return In;
}
So to use this, you'd need SourceEndian to be an indicator to communicate the endianness of the input number. This could be stored in the file (if this is a serialization problem), or communicated over the network (if it's a network serialization issue).
所以要使用它,你需要 SourceEndian 作为一个指示器来传达输入数字的字节序。这可以存储在文件中(如果这是序列化问题),或者通过网络进行通信(如果是网络序列化问题)。
回答by bdonlan
An easy way would be to use ntohl on the two parts seperately:
一个简单的方法是在两个部分分别使用 ntohl:
unsigned long long htonll(unsigned long long v) {
union { unsigned long lv[2]; unsigned long long llv; } u;
u.lv[0] = htonl(v >> 32);
u.lv[1] = htonl(v & 0xFFFFFFFFULL);
return u.llv;
}
unsigned long long ntohll(unsigned long long v) {
union { unsigned long lv[2]; unsigned long long llv; } u;
u.llv = v;
return ((unsigned long long)ntohl(u.lv[0]) << 32) | (unsigned long long)ntohl(u.lv[1]);
}
回答by bdonlan
How about:
怎么样:
#define ntohll(x) ( ( (uint64_t)(ntohl( (uint32_t)((x << 32) >> 32) )) << 32) |
ntohl( ((uint32_t)(x >> 32)) ) )
#define htonll(x) ntohll(x)
回答by Nanno Langstraat
Documentation: man htobe64
on Linux (glibc >= 2.9) or FreeBSD.
文档:man htobe64
在 Linux (glibc >= 2.9) 或 FreeBSD 上。
Unfortunately OpenBSD, FreeBSD and glibc (Linux) did not quite work together smoothly to create one (non-kernel-API) libc standard for this, during an attempt in 2009.
不幸的是,在 2009 年的一次尝试中,OpenBSD、FreeBSD 和 glibc (Linux) 并没有很顺利地协同工作来为此创建一个(非内核 API)libc 标准。
Currently, this short bit of preprocessor code:
目前,这个简短的预处理器代码:
#if defined(__linux__)
# include <endian.h>
#elif defined(__FreeBSD__) || defined(__NetBSD__)
# include <sys/endian.h>
#elif defined(__OpenBSD__)
# include <sys/types.h>
# define be16toh(x) betoh16(x)
# define be32toh(x) betoh32(x)
# define be64toh(x) betoh64(x)
#endif
(tested on Linux and OpenBSD) should hide the differences. It gives you the Linux/FreeBSD-style macros on those 4 platforms.
(在 Linux 和 OpenBSD 上测试)应该隐藏差异。它为您提供了这 4 个平台上的 Linux/FreeBSD 风格的宏。
Use example:
使用示例:
#include <stdint.h> // For 'uint64_t'
uint64_t host_int = 123;
uint64_t big_endian;
big_endian = htobe64( host_int );
host_int = be64toh( big_endian );
It's the most "standard C library"-ish approach available at the moment.
这是目前可用的最“标准 C 库”式的方法。
回答by ijw
It isn't in general necessary to know the endianness of a machine to convert a host integer into network order. Unfortunately that only holds if you write out your net-order value in bytes, rather than as another integer:
通常不需要知道机器的字节序来将主机整数转换为网络顺序。不幸的是,只有当您以字节为单位写出您的网络顺序值而不是另一个整数时,这才成立:
static inline void short_to_network_order(uchar *output, uint16_t in)
{
output[0] = in>>8&0xff;
output[1] = in&0xff;
}
(extend as required for larger numbers).
(根据需要扩展更大的数字)。
This will (a) work on any architecture, because at no point do I use special knowledge about the way an integer is laid out in memory and (b) should mostly optimise away in big-endian architectures because modern compilers aren't stupid.
这将 (a) 适用于任何架构,因为我在任何时候都不会使用有关整数在内存中布局方式的特殊知识,并且 (b) 应该主要在大端架构中进行优化,因为现代编译器并不愚蠢。
The disadvantage is, of course, that this is not the same, standard interface as htonl() and friends (which I don't see as a disadvantage, because the design of htonl() was a poor choice imo).
当然,缺点是这与 htonl() 和朋友的标准接口不同(我认为这不是缺点,因为 htonl() 的设计是一个糟糕的选择)。
回答by Andrei Bozantan
How about a generic version, which doesn't depend on the input size (some of the implementations above assume that unsigned long long
is 64 bits, which is not necessarily always true):
不依赖于输入大小的通用版本怎么样(上面的一些实现假设unsigned long long
是 64 位,这不一定总是正确的):
// converts an arbitrary large integer (preferrably >=64 bits) from big endian to host machine endian
template<typename T> static inline T bigen2host(const T& x)
{
static const int one = 1;
static const char sig = *(char*)&one;
if (sig == 0) return x; // for big endian machine just return the input
T ret;
int size = sizeof(T);
char* src = (char*)&x + sizeof(T) - 1;
char* dst = (char*)&ret;
while (size-- > 0) *dst++ = *src--;
return ret;
}
回答by kuzne4ik
uint32_t SwapShort(uint16_t a)
{
a = ((a & 0x00FF) << 8) | ((a & 0xFF00) >> 8);
return a;
}
uint32_t SwapWord(uint32_t a)
{
a = ((a & 0x000000FF) << 24) |
((a & 0x0000FF00) << 8) |
((a & 0x00FF0000) >> 8) |
((a & 0xFF000000) >> 24);
return a;
}
uint64_t SwapDWord(uint64_t a)
{
a = ((a & 0x00000000000000FFULL) << 56) |
((a & 0x000000000000FF00ULL) << 40) |
((a & 0x0000000000FF0000ULL) << 24) |
((a & 0x00000000FF000000ULL) << 8) |
((a & 0x000000FF00000000ULL) >> 8) |
((a & 0x0000FF0000000000ULL) >> 24) |
((a & 0x00FF000000000000ULL) >> 40) |
((a & 0xFF00000000000000ULL) >> 56);
return a;
}
回答by Sandeep
one line macro for 64bit swap on little endian machines.
在小端机器上用于 64 位交换的一行宏。
#define bswap64(y) (((uint64_t)ntohl(y)) << 32 | ntohl(y>>32))