C++中的64位ntohl()?

Tom*_*Tom 59 c++ linux 64-bit endianness

手册页htonl()似乎暗示您只能将其用于最多32位值.(实际上,ntohl()定义为无符号长整数,在我的平台上是32位.我想如果无符号长整数是8个字节,它将适用于64位整数).

我的问题是我需要将64位整数(在我的例子中,这是一个无符号长long)从big endian转换为little endian.现在,我需要进行特定的转换.但ntohl()如果目标平台WAS大端,如果函数(如)不会转换我的64位值,它会更好.(我宁愿避免添加我自己的预处理器魔法来做到这一点).

我可以用什么?如果它存在,我想要标准的东西,但我愿意接受实施建议.我在过去使用过工会看过这种类型的转换.我想我可以拥有一个带有unsigned long long和char [8]的联合.然后相应地交换字节.(显然会在大端的平台上破解).

Nan*_*aat 53

文档:man htobe64在Linux(glibc> = 2.9)或FreeBSD上.

不幸的是,在2009年的一次尝试中,OpenBSD,FreeBSD和glibc(Linux)并没有很好地协同工作来为此创建一个(非内核API)libc标准.

目前,这一小段预处理器代码:

#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
Run Code Online (Sandbox Code Playgroud)

(在Linux和OpenBSD上测试)应该隐藏差异.它为这4个平台提供了Linux/FreeBSD风格的宏.

使用示例:

  #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 );
Run Code Online (Sandbox Code Playgroud)

它是目前最"标准的C库" - 可用的方法.

  • 这不适用于android(定义`__linux__`但提供openbsd api) (10认同)
  • @Stefan:那太可怕了:( (9认同)

use*_*585 17

我建议阅读这篇文章:http://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

uint64_t
ntoh64(const uint64_t *input)
{
    uint64_t rval;
    uint8_t *data = (uint8_t *)&rval;

    data[0] = *input >> 56;
    data[1] = *input >> 48;
    data[2] = *input >> 40;
    data[3] = *input >> 32;
    data[4] = *input >> 24;
    data[5] = *input >> 16;
    data[6] = *input >> 8;
    data[7] = *input >> 0;

    return rval;
}

uint64_t
hton64(const uint64_t *input)
{
    return (ntoh64(input));
}

int
main(void)
{
    uint64_t ull;

    ull = 1;
    printf("%"PRIu64"\n", ull);

    ull = ntoh64(&ull);
    printf("%"PRIu64"\n", ull);

    ull = hton64(&ull);
    printf("%"PRIu64"\n", ull);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

将显示以下输出:

1
72057594037927936
1
Run Code Online (Sandbox Code Playgroud)

如果删除高4字节,可以使用ntohl()进行测试.

你也可以把它变成一个很好的模板化函数,在C++中适用于任何大小的整数:

template <typename T>
static inline T
hton_any(const T &input)
{
    T output(0);
    const std::size_t size = sizeof(input);
    uint8_t *data = reinterpret_cast<uint8_t *>(&output);

    for (std::size_t i = 0; i < size; i++) {
        data[i] = input >> ((size - i - 1) * 8);
    }

    return output;
}
Run Code Online (Sandbox Code Playgroud)

现在你的128位安全!


pax*_*blo 14

要检测您的字节序,请使用以下联合:

union {
    unsigned long long ull;
    char c[8];
} x;
x.ull = 0x0123456789abcdef; // may need special suffix for ULL.
Run Code Online (Sandbox Code Playgroud)

然后你可以检查内容x.c[]以检测每个字节的去向.

要进行转换,我会使用该检测代码一次查看平台正在使用的字节序,然后编写我自己的函数来进行交换.

您可以将其设置为动态,以便代码可以在任何平台上运行(检测一次然后使用转换代码中的开关来选择正确的转换)但是,如果您只是使用一个平台,我只会这样做在单独的程序中检测一次然后编写一个简单的转换例程,确保您记录它只在该平台上运行(或已经过测试).

这是一些示例代码,我用它来说明它.它虽然没有经过彻底的测试,但应该足以让你入门.

#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;
}
Run Code Online (Sandbox Code Playgroud)

请记住,这只是检查纯大/小端.如果你有一些奇怪的变量存储字节,例如,{5,2,3,1,0,7,6,4}顺序,cvt()将会有点复杂.这样的架构不值得存在,但我并没有打折微处理器行业中朋友的疯狂:-)

还要记住,这是技术上未定义的行为,因为您不应该通过除最后一个字段之外的任何字段访问union成员.它可能适用于大多数实现,但是,从纯粹主义的角度来看,你应该只是咬紧牙关并使用宏来定义自己的例程,例如:

// 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
Run Code Online (Sandbox Code Playgroud)

  • 实际上,"返回src"应该是针对big-endian架构而不是little-endian.另外,在little-endian CPU上进行转换的更简洁方法是通过在src的低32位使用htonl()和使用htonl在结果的低32位计算结果的高32位()在src的高32位(希望有一点意义......). (2认同)

Fra*_*cis 12

一些BSD系统可以满足betoh64您的需求.

  • Linux(glibc)也是.它位于<endian.h>标头中. (4认同)

oli*_*bre 11

快速回答

#include <endian.h>    // __BYTE_ORDER __LITTLE_ENDIAN
#include <byteswap.h>  // bswap_64()

uint64_t value = 0x1122334455667788;

#if __BYTE_ORDER == __LITTLE_ENDIAN
value = bswap_64(value);  // Compiler builtin GCC/Clang
#endif
Run Code Online (Sandbox Code Playgroud)

头文件

zhaorufei报道(见她/他的评论)endian.h不是C++标准头和宏__BYTE_ORDER,__LITTLE_ENDIAN可能是未定义的.因此,该#if语句不可预测,因为未定义的宏被视为0.

如果你想分享你的C++优雅技巧来检测字节顺序,请编辑这个答案.

可移植性

此外,宏bswap_64()可用于GCC和Clang编译器,但不适用于Visual C++编译器.要提供便携式源代码,您可能会受到以下代码段的启发:

#ifdef _MSC_VER
  #include <stdlib.h>
  #define bswap_16(x) _byteswap_ushort(x)
  #define bswap_32(x) _byteswap_ulong(x)
  #define bswap_64(x) _byteswap_uint64(x)
#else
  #include <byteswap.h>  // bswap_16 bswap_32 bswap_64
#endif
Run Code Online (Sandbox Code Playgroud)

另请参阅更便携的源代码:跨平台_byteswap_uint64

C++ 14 constexpr模板函数

通用hton()16位,32位,64位以上......

#include <endian.h>   // __BYTE_ORDER __LITTLE_ENDIAN
#include <algorithm>  // std::reverse()

template <typename T>
constexpr T htonT (T value) noexcept
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
  char* ptr = reinterpret_cast<char*>(&value);
  std::reverse(ptr, ptr + sizeof(T));
#endif
  return value;
}
Run Code Online (Sandbox Code Playgroud)

C++ 11 constexpr模板函数

  • C++ 11不允许constexpr函数中的局部变量.
    因此,技巧是使用具有默认值的参数.
  • 此外,C++ 11 constexpr函数必须包含一个表达式.
    因此,正文由一个带有逗号分隔语句的返回组成.
template <typename T>
constexpr T htonT (T value, char* ptr=0) noexcept
{
  return 
#if __BYTE_ORDER == __LITTLE_ENDIAN
    ptr = reinterpret_cast<char*>(&value), 
    std::reverse(ptr, ptr + sizeof(T)),
#endif
    value;
}
Run Code Online (Sandbox Code Playgroud)

在clang-3.5和GCC-4.9上都没有编译警告-Wall -Wextra -pedantic
(参见coliru上的编译和运行输出).

C++ 11 constexpr模板SFINAE函数

但是,上述版本不允许创建constexpr变量:

constexpr int32_t hton_six = htonT( int32_t(6) );
Run Code Online (Sandbox Code Playgroud)

最后,我们需要根据16/32/64位分离(专门化)函数.
但我们仍然可以保留通用功能.
(请参阅coliru上的完整代码段)

下面的C++ 11片段使用特征 std::enable_if来利用替换失败不是错误(SFINAE).

template <typename T>
constexpr typename std::enable_if<sizeof(T) == 2, T>::type
htonT (T value) noexcept
{
   return  ((value & 0x00FF) << 8)
         | ((value & 0xFF00) >> 8);
}

template <typename T>
constexpr typename std::enable_if<sizeof(T) == 4, T>::type
htonT (T value) noexcept
{
   return  ((value & 0x000000FF) << 24)
         | ((value & 0x0000FF00) <<  8)
         | ((value & 0x00FF0000) >>  8)
         | ((value & 0xFF000000) >> 24);
}

template <typename T>
constexpr typename std::enable_if<sizeof(T) == 8, T>::type
htonT (T value) noexcept
{
   return  ((value & 0xFF00000000000000ull) >> 56)
         | ((value & 0x00FF000000000000ull) >> 40)
         | ((value & 0x0000FF0000000000ull) >> 24)
         | ((value & 0x000000FF00000000ull) >>  8)
         | ((value & 0x00000000FF000000ull) <<  8)
         | ((value & 0x0000000000FF0000ull) << 24)
         | ((value & 0x000000000000FF00ull) << 40)
         | ((value & 0x00000000000000FFull) << 56);
}
Run Code Online (Sandbox Code Playgroud)

或者是基于内置编译器宏和C++ 14语法的更短版本std::enable_if_t<xxx>作为快捷方式std::enable_if<xxx>::type:

template <typename T>
constexpr typename std::enable_if_t<sizeof(T) == 2, T>
htonT (T value) noexcept
{
    return bswap_16(value);  // __bswap_constant_16
}

template <typename T>
constexpr typename std::enable_if_t<sizeof(T) == 4, T>
htonT (T value) noexcept
{
    return bswap_32(value);  // __bswap_constant_32
}

template <typename T>
constexpr typename std::enable_if_t<sizeof(T) == 8, T>
htonT (T value) noexcept
{
    return bswap_64(value);  // __bswap_constant_64
}
Run Code Online (Sandbox Code Playgroud)

测试第一个版本的代码

std::uint8_t uc = 'B';                  std::cout <<std::setw(16)<< uc <<'\n';
uc = htonT( uc );                       std::cout <<std::setw(16)<< uc <<'\n';

std::uint16_t us = 0x1122;              std::cout <<std::setw(16)<< us <<'\n';
us = htonT( us );                       std::cout <<std::setw(16)<< us <<'\n';

std::uint32_t ul = 0x11223344;          std::cout <<std::setw(16)<< ul <<'\n';
ul = htonT( ul );                       std::cout <<std::setw(16)<< ul <<'\n';

std::uint64_t uL = 0x1122334455667788; std::cout <<std::setw(16)<< uL <<'\n';
uL = htonT( uL );                      std::cout <<std::setw(16)<< uL <<'\n';
Run Code Online (Sandbox Code Playgroud)

测试第二个版本的代码

constexpr uint8_t  a1 = 'B';               std::cout<<std::setw(16)<<a1<<'\n';
constexpr auto     b1 = htonT(a1);         std::cout<<std::setw(16)<<b1<<'\n';

constexpr uint16_t a2 = 0x1122;            std::cout<<std::setw(16)<<a2<<'\n';
constexpr auto     b2 = htonT(a2);         std::cout<<std::setw(16)<<b2<<'\n';

constexpr uint32_t a4 = 0x11223344;        std::cout<<std::setw(16)<<a4<<'\n';
constexpr auto     b4 = htonT(a4);         std::cout<<std::setw(16)<<b4<<'\n';

constexpr uint64_t a8 = 0x1122334455667788;std::cout<<std::setw(16)<<a8<<'\n';
constexpr auto     b8 = htonT(a8);         std::cout<<std::setw(16)<<b8<<'\n';
Run Code Online (Sandbox Code Playgroud)

产量

               B
               B
            1122
            2211
        11223344
        44332211
1122334455667788
8877665544332211
Run Code Online (Sandbox Code Playgroud)

代码生成

在线C++编译器gcc.godbolt.org表示生成的代码.

g++-4.9.2 -std=c++14 -O3

std::enable_if<(sizeof (unsigned char))==(1), unsigned char>::type htonT<unsigned char>(unsigned char):
    movl    %edi, %eax
    ret
std::enable_if<(sizeof (unsigned short))==(2), unsigned short>::type htonT<unsigned short>(unsigned short):
    movl    %edi, %eax
    rolw    $8, %ax
    ret
std::enable_if<(sizeof (unsigned int))==(4), unsigned int>::type htonT<unsigned int>(unsigned int):
    movl    %edi, %eax
    bswap   %eax
    ret
std::enable_if<(sizeof (unsigned long))==(8), unsigned long>::type htonT<unsigned long>(unsigned long):
    movq    %rdi, %rax
    bswap   %rax
    ret
Run Code Online (Sandbox Code Playgroud)

clang++-3.5.1 -std=c++14 -O3

std::enable_if<(sizeof (unsigned char))==(1), unsigned char>::type htonT<unsigned char>(unsigned char): # @std::enable_if<(sizeof (unsigned char))==(1), unsigned char>::type htonT<unsigned char>(unsigned char)
    movl    %edi, %eax
    retq

std::enable_if<(sizeof (unsigned short))==(2), unsigned short>::type htonT<unsigned short>(unsigned short): # @std::enable_if<(sizeof (unsigned short))==(2), unsigned short>::type htonT<unsigned short>(unsigned short)
    rolw    $8, %di
    movzwl  %di, %eax
    retq

std::enable_if<(sizeof (unsigned int))==(4), unsigned int>::type htonT<unsigned int>(unsigned int): # @std::enable_if<(sizeof (unsigned int))==(4), unsigned int>::type htonT<unsigned int>(unsigned int)
    bswapl  %edi
    movl    %edi, %eax
    retq

std::enable_if<(sizeof (unsigned long))==(8), unsigned long>::type htonT<unsigned long>(unsigned long): # @std::enable_if<(sizeof (unsigned long))==(8), unsigned long>::type htonT<unsigned long>(unsigned long)
    bswapq  %rdi
    movq    %rdi, %rax
    retq
Run Code Online (Sandbox Code Playgroud)

注意:我的原始答案constexpr符合C++ 11 .

这个答案是在Public Domain CC0 1.0 Universal


San*_*eep 6

在小端机器上进行64位交换的一行宏.

#define bswap64(y) (((uint64_t)ntohl(y)) << 32 | ntohl(y>>32))
Run Code Online (Sandbox Code Playgroud)


And*_*tan 5

通用版本怎么样,它不依赖于输入大小(上面的一些实现假设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;
    }
Run Code Online (Sandbox Code Playgroud)


小智 5

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;
}
Run Code Online (Sandbox Code Playgroud)

  • 为什么16位函数返回32位int? (3认同)