64位ntohl()在C + +?

htonl()的手册页似乎build议您只能使用它最多32位值。 (实际上, ntohl()被定义为unsigned long,在我的平台上是32位。我想如果unsigned long是8个字节,那么它可以用于64位整数)。

我的问题是,我需要从大端到小端转换64位整数(在我的情况下,这是一个无符号long long)。 现在,我需要做具体的转换。 但是,如果函数(如ntohl() )不能转换我的64位值,如果目标平台是WAS大端,这将更好。 (我宁愿避免添加我自己的预处理器魔术来做到这一点)。

我可以使用什么? 我想要一些标准的东西,但是我愿意接受实施build议。 我曾经看到过去使用工会进行的这种转换。 我想我可以有一个无符号long long和char [8]的联合。 然后交换相应的字节。 (显然会在大端的平台上打破)。

文档:Linux上的man htobe64 (glibc> = 2.9)或FreeBSD。

不幸的是,在2009年的一次尝试中,OpenBSD,FreeBSD和glibc(Linux)并没有完全合作,为此创build一个(非内核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 

(在Linux和OpenBSD上testing)应该隐藏差异。 它为您提供了4个平台上的Linux / FreeBSD风格的macros。

使用示例:

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

这是目前可用的最“标准的C库”方法。

要检测您的endian-ness,请使用以下联合:

 union { unsigned long long ull; char c[8]; } x; x.ull = 0x0123456789abcdef; // may need special suffix for ULL. 

然后你可以检查xc[]的内容来检测每个字节的位置。

为了进行转换,我会使用这个检测代码来查看平台正在使用的是什么endian – ness,然后编写我自己的函数做交换。

你可以使它成为dynamic的,以便代码可以在任何平台上运行(一次检测一次,然后在你的转换代码中使用一个开关来select正确的转换),但是如果你只打算使用一个平台,在一个单独的程序中检测一次,然后编写一个简单的转换例程,确保您logging它只在该平台上运行(或已经过testing)。

下面是一些示例代码,我用它来表示它。 虽然testing不彻底,但应该足以让你开始。

 #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 = (xc[7] == 0x01) ? TYP_BIGE : TYP_SMLE; } if (typ == TYP_SMLE) return src; x.ull = src; c = xc[0]; xc[0] = xc[7]; xc[7] = c; c = xc[1]; xc[1] = xc[6]; xc[6] = c; c = xc[2]; xc[2] = xc[5]; xc[5] = c; c = xc[3]; xc[3] = xc[4]; xc[4] = c; return x.ull; } int main (void) { unsigned long long ull = 1; ull = cvt (ull); printf ("%llu\n",ull); return 0; } 

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

另外请记住,这在技术上是未定义的行为,因为你不应该通过任何领域以外的任何字段访问联盟成员书面。 它可能会适用于大多数的实现,但是,从纯粹的angular度来看,你可能应该咬紧牙关,用macros来定义自己的例程,如:

 // 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 

我会推荐阅读: 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; } 

将显示以下输出:

 1 72057594037927936 1 

你可以用ntohl()来testing这个,如果你删除高4字节的话。

你也可以把它变成一个很好的C ++模板化函数,它可以处理任何大小的整数:

 template <typename T> static inline T hton_any(const T &input) { T output(0); const std::size_t size = sizeof(input) - 1; uint8_t *data = reinterpret_cast<uint8_t *>(&output); for (std::size_t i = 0; i < size; i++) { data[i] = input >> ((size - i) * 8); } return output; } 

现在你的128位安全了!

一些BSD系统已经betoh64了你所需要的水平。

快速回答

 uint64_t value = 0x1122334455667788; #if __BYTE_ORDER == __LITTLE_ENDIAN value = __bswap_constant_64(value); // Compiler builtin #endif 

这个答案的其余部分是关于通用hton()的16位,32位,64位…在现代C + +。

C ++ 14 constexpr模板函数

 #include <endian.h> // __BYTE_ORDER #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; } 

C ++ 11的constexpr模板函数

  • C ++ 11在constexpr函数中不允许局部variables。
    所以诀窍是使用默认值的参数。
  • 而且C ++ 11的constexpr函数必须包含一个expression式。
    因此,身体由一个有逗号分隔的陈述组成
 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; } 

在clang-3.5和GCC-4.9上都没有编译警告,使用-Wall -Wextra -pedantic (请参阅-Wall -Wextra -pedantic编译和运行输出)。

C ++ 11 constexpr模板SFINAE函数

但是上面的版本不允许创buildconstexprvariables,如下所示:

 constexpr int32_t hton_six = htonT( int32_t(6) ); 

最后,我们需要根据16/32/64位来分离(专门化)函数。
但是我们仍然可以保留generics函数。
=>在coliru上查看完整的代码片段。

下面的C ++ 11片段使用特征 std::enable_if来利用replace失败不是错误 (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); } 

或者是基于内置编译器macros和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_constant_16(value); } template <typename T> constexpr typename std::enable_if_t<sizeof(T) == 4, T> htonT (T value) noexcept { return __bswap_constant_32(value); } template <typename T> constexpr typename std::enable_if_t<sizeof(T) == 8, T> htonT (T value) noexcept { return __bswap_constant_64(value); } 

第一个版本的testing代码

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

第二个版本的testing代码

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

产量

  B B 1122 2211 11223344 44332211 1122334455667788 8877665544332211 

代码生成

在线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 

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 

注:我原来的答案不是C ++ 11- constexpr兼容。

这个答案在公共领域CC0 1.0 Universal

在小端机上用64bit交换一行macros。

 #define bswap64(y) (((uint64_t)ntohl(y)) << 32 | ntohl(y>>32)) 

通用版本如何,不依赖于input大小(上面的一些实现假设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; } 
 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; } 

我喜欢工会的答案,非常整齐。 通常情况下,我只是在小端和大端之间进行转换,尽pipe我认为联合解决scheme的分配更less,速度更快:

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

然后,为了检测是否需要在没有macros魔法的情况下进行翻转操作,可以像Pax那样做一个类似的事情,当short被分配到0x0001时,在相对的endian系统上将是0x0100。

所以:

 unsigned long long numberToSystemEndian ( unsigned long long In, unsigned short SourceEndian ) { if (SourceEndian != 1) { //from an opposite endian system endianFlip(In); } return In; } 

所以要使用这个,你需要SourceEndian作为一个指示器来传达input号码的字节顺序。 这可以存储在文件中(如果这是一个序列化问题),或通过networking传递(如果这是一个networking序列化问题)。

怎么样:

 #define ntohll(x) ( ( (uint64_t)(ntohl( (uint32_t)((x << 32) >> 32) )) << 32) | ntohl( ((uint32_t)(x >> 32)) ) ) #define htonll(x) ntohll(x) 

一个简单的方法是在这两个部分单独使用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]); } 
 template <typename T> static T ntoh_any(T t) { static const unsigned char int_bytes[sizeof(int)] = {0xFF}; static const int msb_0xFF = 0xFF << (sizeof(int) - 1) * CHAR_BIT; static bool host_is_big_endian = (*(reinterpret_cast<const int *>(int_bytes)) & msb_0xFF ) != 0; if (host_is_big_endian) { return t; } unsigned char * ptr = reinterpret_cast<unsigned char *>(&t); std::reverse(ptr, ptr + sizeof(t) ); return t; } 

适用于2个字节,4个字节,8个字节和16个字节(如果您有128位整数)。 应该是OS /平台独立。

假设你在使用64位操作系统的Linux上进行编码; 大多数系统都有htole(x)或者ntobe(x)等,这些通常是各种bswap的macros

 #include <endian.h> #include <byteswap.h> unsigned long long htonll(unsigned long long val) { if (__BYTE_ORDER == __BIG_ENDIAN) return (val); else return __bswap_64(val); } unsigned long long ntohll(unsigned long long val) { if (__BYTE_ORDER == __BIG_ENDIAN) return (val); else return __bswap_64(val); } 

边注; 这些只是调用交换字节顺序的函数。 如果你正在使用little endian,例如一个大的endiannetworking,但是如果你使用的是大的结束编码,那么这将会不必要地颠倒字节sorting,所以if __BYTE_ORDER == __LITTLE_ENDIAN可能需要一些“ if __BYTE_ORDER == __LITTLE_ENDIAN ”检查来使你的代码更加便携,取决于你的需求。

更新:编辑显示endian检查的例子

适用于任何值的通用function。

 template <typename T> T swap_endian (T value) { union { T src; unsigned char dst[sizeof(T)]; } source, dest; source.src = value; for (size_t k = 0; k < sizeof(T); ++k) dest.dst[k] = source.dst[sizeof(T) - k - 1]; return dest.src; } 

通常不需要知道机器的字节序来将主机整数转换成networking顺序。 不幸的是,只有你写出你的净价值以字节为单位,而不是另一个整数:

 static inline void short_to_network_order(uchar *output, uint16_t in) { output[0] = in>>8&0xff; output[1] = in&0xff; } 

(根据需要延伸更大的数字)。

这将会(a)在任何架构上工作,因为在任何时候我都不会使用有关整数在内存中布局的方式的特殊知识,(b)由于现代编译器不愚蠢,所以大部分应该在大端架构中进行优化。

当然,缺点是,这是不一样的,标准接口作为htonl()和朋友(我不认为这是一个缺点,因为htonl()的devise是一个不好的select)。