在 C + + 中有没有类似于 htonl 的64位整数的“标准”函数?

我正在研究 memcache 协议的一个实现,该协议在某些情况下使用64位整数值。这些值必须按“网络字节顺序”存储。

我希望有一些 uint64_t htonll(uint64_t value)函数来做改变,但不幸的是,如果它存在,我找不到它。

我有一两个问题:

  • 是否有任何 便携式的(Windows,Linux,AIX)标准函数来完成这项工作?
  • 如果没有这样的函数,您将如何实现它?

我想到了一个基本的实现,但是我不知道如何在编译时检查 endianness 来使代码可移植。因此,这里非常欢迎你的帮助;)

谢谢你。


这是我写的最终解决方案,感谢布莱恩的解决方案。

uint64_t htonll(uint64_t value)
{
// The answer is 42
static const int num = 42;


// Check the endianness
if (*reinterpret_cast<const char*>(&num) == num)
{
const uint32_t high_part = htonl(static_cast<uint32_t>(value >> 32));
const uint32_t low_part = htonl(static_cast<uint32_t>(value & 0xFFFFFFFFLL));


return (static_cast<uint64_t>(low_part) << 32) | high_part;
} else
{
return value;
}
}
50718 次浏览

You are probably looking for bswap_64 I think it is supported pretty much everywhere but I wouldn't call it standard.

You can easily check the endianness by creating an int with a value of 1, casting your int's address as a char* and checking the value of the first byte.

For example:

int num = 42;
if(*(char *)&num == 42)
{
//Little Endian
}
else
{
//Big Endian
}

Knowing this you could also make a simple function that does the swapping.


You could also always use boost which contains endian macros which are portable cross platform.

EDIT: combining the two (used Brian's code):

uint64_t htonll(uint64_t value)
{
int num = 42;
if(*(char *)&num == 42)
return (htonl(value & 0xFFFFFFFF) << 32LL) | htonl(value >> 32);
else
return value;
}

Warning: untested code! Please test before using.

This seems to work in C; did I do anything wrong?

uint64_t htonll(uint64_t value) {
int num = 42;
if (*(char *)&num == 42) {
uint32_t high_part = htonl((uint32_t)(value >> 32));
uint32_t low_part = htonl((uint32_t)(value & 0xFFFFFFFFLL));
return (((uint64_t)low_part) << 32) | high_part;
} else {
return value;
}
}

To reduce the overhead of the "if num == ..." Use the pre-processor defines:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#else
#endif
#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
#define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32))

The test (1==htonl(1)) simply determines (at runtime sadly) if the hardware architecture requires byte swapping. There aren't any portable ways to determine at compile-time what the architecture is, so we resort to using "htonl", which is as portable as it gets in this situation. If byte-swapping is required, then we swap 32 bits at a time using htonl (remembering to swap the two 32 bit words as well).


Here's another way to perform the swap that is portable across most compilers and operating systems, including AIX, BSDs, Linux, and Solaris.

#if __BIG_ENDIAN__
# define htonll(x) (x)
# define ntohll(x) (x)
#else
# define htonll(x) (((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
# define ntohll(x) (((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32))
#endif

The important part is to use __BIG_ENDIAN__ or __LITTLE_ENDIAN__; and not __BYTE_ORDER__, __ORDER_BIG_ENDIAN__ or __ORDER_LITTLE_ENDIAN__. Some compilers and operating systems lack __BYTE_ORDER__ and friends.

You can try with uint64_t htobe64(uint64_t host_64bits) & uint64_t be64toh(uint64_t big_endian_64bits) for vice-versa.

Well, I think it's better to use the endian switching at compile time as much as possible, but I prefer to use function instead of macro, because in a macro the parameters are just replaced by the arguments; so arguments can be evaluated multiple times and create weird result if they are present multiple times in the macro (as done in some of the previous provided solutions).

uint64_t htonll(uint64_t x)
{
#if __BIG_ENDIAN__
return x;
#else
return ((uint64_t)htonl((x) & 0xFFFFFFFFLL) << 32) | htonl((x) >> 32);
#endif
}


uint64_t ntohll(uint64_t x)
{
#if __BIG_ENDIAN__
return x;
#else
return ((uint64_t)ntohl((x) & 0xFFFFFFFFLL) << 32) | ntohl((x) >> 32);
#endif
}

So this allow calling htonll(x++) without increment x several time like it will be done using the previous macros.