C++ 中是否有类似“标准”htonl 的 64 位整数函数?

问题描述 投票:0回答:8

我正在开发 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;
    }
}
c++ 64-bit portability endianness htonl
8个回答
23
投票
#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__
__LITTLE_ENDIAN__
;而不是
__BYTE_ORDER__
__ORDER_BIG_ENDIAN__
__ORDER_LITTLE_ENDIAN__
。有些编译器和操作系统缺少
__BYTE_ORDER__
和朋友。


19
投票

您可能正在寻找

bswap_64
我认为它几乎到处都受支持,但我不会称其为标准。

您可以通过创建值为 1 的 int、将 int 的地址转换为

char*
并检查第一个字节的值来轻松检查字节顺序。

例如:

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

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


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


9
投票

您可以尝试使用

uint64_t htobe64(uint64_t host_64bits)
&
uint64_t be64toh(uint64_t big_endian_64bits)
反之亦然。


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

1
投票

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

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#else
#endif

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,就像使用之前的宏一样。


0
投票

我建议使用endian.h

中定义的函数

由于网络字节顺序按照惯例是大端字节序,因此您只需从主机调用适当的函数即可“be”。它包括 16、32 和 64 位版本。

我创建了以下单个 ntoh 模板化函数,适用于所有整数类型,因此我不必担心要调用哪个函数。

template <typename T> std::enable_if_t<sizeof(T) == 2, T> ntoh(T v) { return be16toh(v); }
template <typename T> std::enable_if_t<sizeof(T) == 4, T> ntoh(T v) { return be32toh(v); }
template <typename T> std::enable_if_t<sizeof(T) == 8, T> ntoh(T v) { return be64toh(v); }

template <typename T> std::enable_if_t<sizeof(T) == 2, T> hton(T v) { return htobe16(v); }
template <typename T> std::enable_if_t<sizeof(T) == 4, T> hton(T v) { return htobe32(v); }
template <typename T> std::enable_if_t<sizeof(T) == 8, T> hton(T v) { return htobe64(v); }

在 Linux 上,对该函数的调用没有任何损失,因为不需要进行任何转换。 在 Windows 上,谁关心 Windows ;-P

希望它对某人有帮助。


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

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

© www.soinside.com 2019 - 2024. All rights reserved.