73

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

我希望有一些uint64_t htonll(uint64_t value)功能可以进行更改,但不幸的是,如果它存在,我找不到它。

所以我有1或2个问题:

  • 是否有任何可移植(Windows、Linux、AIX)标准功能来做到这一点?
  • 如果没有这样的功能,你将如何实现它?

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

谢谢你。


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

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;
    }
}
4

7 回答 7

22
#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))

测试 (1==htonl(1)) 只是确定(可悲的是在运行时)硬件架构是否需要字节交换。没有任何可移植的方法可以在编译时确定架构是什么,因此我们求助于使用“htonl”,它在这种情况下具有可移植性。如果需要字节交换,那么我们使用 htonl 一次交换 32 位(记住也要交换两个 32 位字)。


这是执行交换的另一种方法,该方法可在大多数编译器和操作系统(包括 AIX、BSD、Linux 和 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

重要的部分是使用__BIG_ENDIAN__or __LITTLE_ENDIAN__; 而不是__BYTE_ORDER____ORDER_BIG_ENDIAN____ORDER_LITTLE_ENDIAN__。一些编译器和操作系统缺乏__BYTE_ORDER__和朋友。

于 2015-02-18T19:28:04.800 回答
19

您可能正在寻找bswap_64我认为几乎所有地方都支持它,但我不会称之为标准。

您可以通过创建一个值为 1 的 int 来轻松检查字节顺序,将 int 的地址转换为 achar*并检查第一个字节的值。

例如:

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

知道了这一点,您还可以创建一个简单的函数来进行交换。


您也可以始终使用包含可移植跨平台的字节序宏的 boost。

于 2010-06-11T12:14:10.203 回答
7

您可以尝试使用uint64_t htobe64(uint64_t host_64bits)& uint64_t be64toh(uint64_t big_endian_64bits)反之亦然。

于 2017-10-22T18:06:58.710 回答
5

这似乎在 C 中有效;我做错什么了吗?

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;
    }
}
于 2011-05-08T16:47:26.613 回答
1

为了减少“if num == ...”的开销,使用预处理器定义:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#else
#endif
于 2014-06-12T13:27:28.027 回答
0

好吧,我认为在编译时尽可能使用字节序切换会更好,但我更喜欢使用函数而不是宏,因为在宏中,参数只是被参数替换;因此,如果参数在宏中多次出现(如之前提供的一些解决方案中所做的那样),参数可以被多次评估并产生奇怪的结果。

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
}

所以这允许调用 htonll(x++) 而不增加 x 几次,就像使用前面的宏一样。

于 2020-01-24T00:23:07.717 回答
-3

编辑:结合两者(使用布莱恩的代码):

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

警告:未经测试的代码!请在使用前进行测试。

于 2010-06-11T12:26:59.150 回答