【问题标题】:64 bit ntohl() in C++?C ++中的64位ntohl()?
【发布时间】:2010-10-23 00:48:28
【问题描述】:

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

我的问题是我需要将 64 位整数(在我的情况下,这是一个 unsigned long long)从大端转换为小端。现在,我需要进行特定的转换。但是,如果目标平台是大端的,如果函数(如ntohl())不会转换我的 64 位值,那就更好了。 (我宁愿避免添加我自己的预处理器魔法来做到这一点)。

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

【问题讨论】:

  • 你的平台是什么?大多数系统都有特定于平台的 BE 到 LE 转换例程。如果做不到这一点,你可以很容易地写一个。
  • 看看我在this other question的回复
  • 只是我的2cts,在C标准中写得很清楚(不知道是89还是99),long应该足以存储指针。一个短语,但是没有出现在 C++ 标准中。我见过的 Linux 编译器尊重这一点,long 是 64 位构建的 64 位。然而,微软选择了一个奇怪的解决方案,其中 long 到处都是 32 位。
  • the question is given here 感兴趣的人的实际答案

标签: c++ linux 64-bit endianness


【解决方案1】:

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

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

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

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

您可以使其动态化,以便代码可以在任何平台上运行(检测一次,然后使用转换代码中的开关来选择正确的转换)但是,如果您只打算使用一个平台,我' d 只需在单独的程序中进行一次检测,然后编写一个简单的转换例程,确保您记录它仅在该平台上运行(或已经过测试)。

这是我为说明它而编写的一些示例代码。虽然没有经过彻底的测试,但应该足以让您入门。

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

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

另外请记住,这在技术上是未定义的行为,因为除了最后一个写入的字段之外,您不应该通过任何字段访问联合成员。它可能适用于大多数实现,但从纯粹主义者的角度来看,您可能应该硬着头皮使用宏来定义自己的例程,例如:

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

【讨论】:

  • “可能需要特殊的 ULL 后缀”——C89 和 C++ 都没有定义可移植的。但是,您可以执行 x.ull = ((unsigned long long) 0x01234567)
  • 谢谢,onebyone,我最终只使用了 0x01 并检测到它。
  • 其实“return src”应该针对大端架构,而不是小端架构。此外,在 little-endian CPU 上进行转换的一种更简洁的方法是通过在 src 的低 32 位上使用 htonl() 计算结果的高 32 位,并使用 htonl 计算结果的低 32 位() 在 src 的高 32 位上(希望这有点道理......)。
  • 这不对,是吗,兰斯?该问题询问的是小端的值 - 这意味着在小端系统上将其单独放置并在大端系统上交换它。
  • 为什么不使用 unsigned int a = 1; if(*((char *)&a) == 1) printf("little edian");
【解决方案2】:

一些 BSD 系统有 betoh64 可以满足您的需要。

【讨论】:

  • Linux (glibc) 也是。它位于 标头中。
  • 嗯...我在任何 endian.h 标头中都找不到该函数。我现在在我的英特尔 mac 上(运行豹)。我还需要让它在学校的 Linux 机器上工作。我不确定哪个发行版正在运行,但我相当肯定它们是 i386 机器、little endian 和 sizeof(unsigned long long) == 8。另外,我需要的函数是 be64toh()。有什么建议?我更喜欢这个解决方案。
  • 我的错 - 你想要的应该是 betoh64。在 FreeBSD 上,它位于 /usr/include/sys/endian.h 中。手册页是 byteorder(9)。根据 FreeBSD 的注释,这些最初来自 NetBSD,并在 5.x 之后出现在 FreeBSD 上。据我所知,MacOSX 使用大量 FreeBSD 文件作为其后端 (darwin) 基础 - 因此您很有可能可以使用它。
  • @Francis:我的消息来源表明它甚至存在于 4.3BSD 中。 @Tom:Autoconf 查找 endian.h、sys/endian.h 和 machinfo/endian.h;您可能必须在不同平台上使用不同的包含路径。
【解决方案3】:

我喜欢工会的回答,很整洁。通常我只是在小端和大端之间进行转换,尽管我认为联合解决方案的分配更少并且可能更快:

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

然后,要检测是否需要在没有宏魔法的情况下进行翻转,您可以执行与 Pax 类似的操作,其中当将短路分配给 0x0001 时,在相反的字节序系统上将是 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 作为指示符来传达输入数字的字节顺序。这可以存储在文件中(如果这是一个序列化问题),或者通过网络进行通信(如果这是一个网络序列化问题)。

【讨论】:

    【解决方案4】:

    一个简单的方法是在两个部分上分别使用 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]);
    }
    

    【讨论】:

    • 您的第一个函数是 htonll 并在内部使用 ntohl()。这两个功能是可以互换的,对吗?如果是这样,为什么它们的实施方式不同?
    • 糟糕,已修复。严格来说,除了大端或小端之外,还有其他字节序选项 - 虽然您不再看到它们,但在一些非常旧的系统上,htonl()ntohl() 的行为可能会有所不同。
    【解决方案5】:

    怎么样:

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

    【讨论】:

      【解决方案6】:

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

      不幸的是,在 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
      

      (在 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 );
      

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

      【讨论】:

      • 这不适用于android(它定义了__linux__但提供了openbsd api)
      • @Stefan:太可怕了:(
      【解决方案7】:

      通常不需要知道机器的字节顺序来将主机整数转换为网络顺序。不幸的是,只有当您以字节而不是另一个整数写出您的净订单值时,这才成立:

      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() 的设计是一个糟糕的选择 imo)。

      【讨论】:

        【解决方案8】:

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

        【讨论】:

        • 迄今为止最好的解决方案。我只想用 for 替换 while,这样编译器就可以依靠 sizeof(T) 来展开循环。
        【解决方案9】:
        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;
        }
        

        【讨论】:

        • 为什么 16 位函数返回 32 位 int?
        【解决方案10】:

        用于小端机器上 64 位交换的一行宏。

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

        【讨论】:

        • @BjornRoche 为大端机器构建类似的宏将很容易。 #include &lt;endian.h&gt; #if __BYTE_ORDER == __LITTLE_ENDIAN 整理 bswap64() API 并使其独立于平台。
        【解决方案11】:

        我会推荐阅读这个: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
        

        如果你删除了前 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;
        }
        

        现在你的 128 位也安全了!

        【讨论】:

        • 我认为您的模板版本已损坏,它忽略了最后一个字节。为了解决这个问题,我更改了 size = sizeof(T);input &gt;&gt; ((size-i-1)*8)
        • 纯粹是推测编译器对大于寄存器大小的类型的抽象如何将这些部分存储在内存中。谁说他们要严格遵守小端或大端?它甚至可以是不依赖于平台的设计,例如它在架构上是自然的,而在另一个架构上是不自然的。没关系,因为将大整数“加载”到寄存器中的代码是相同的,并且是可移植的。但是这个选择取决于编译器。
        • 以 uint8_t 访问内存违反了严格的别名规则,是未定义的行为。
        • 有没有办法让它与结构一起工作?将处理应用于每个字节...它会在第一行(T 的初始化)产生错误,并且缺少 operator&gt;&gt;
        【解决方案12】:
        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 位整数)。应该独立于操作系统/平台。

        【讨论】:

          【解决方案13】:

          快速解答

          #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
          

          头文件

          正如zhaorufei 所报告的(见她/他的评论)endian.h 不是 C++ 标准头文件,宏 __BYTE_ORDER__LITTLE_ENDIAN 可能未定义。因此#if 语句是不可预测的,因为未定义的宏被视为0

          如果您想分享您的 C++ 优雅技巧来检测字节顺序,请编辑此答案。

          便携性

          此外,宏 bswap_64() 可用于 GCC 和 Clang 编译器,但不适用于 Visual C++ 编译器。要提供可移植的源代码,您可能会受到以下 sn-p 的启发:

          #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
          

          另见更便携的源代码:Cross-platform _byteswap_uint64

          C++14constexpr模板函数

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

          C++11constexpr模板函数

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

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

          C++11 constexpr 模板 SFINAE 函数

          但是以上版本不允许创建constexpr 变量为:

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

          最后,我们需要根据 16/32/64 位来分离(特化)函数。
          但是我们仍然可以保留泛型函数。
          (请参阅coliru 上的完整 sn-p)

          下面的 C++11 sn-p 使用traits std::enable_if 来利用Substitution Failure Is Not An Error (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);
          }
          

          或者基于内置编译器宏和 C++14 语法 std::enable_if_t&lt;xxx&gt; 作为std::enable_if&lt;xxx&gt;::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
          }
          

          第一版测试代码

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

          第二版测试代码

          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
          

          注意:我的 original answer 不符合 C++11-constexpr

          这个答案在Public Domain CC0 1.0 Universal

          【讨论】:

          • htonT()中,你会给它一个char*输入参数而不是使用局部变量吗?
          • 感谢@RemyLebeau 的反馈。 C++11 不允许在 constexpr 函数中使用局部变量 :-( 一年半之后,C++14 的使用越来越多,而不仅仅是 C++11。因此我更新了答案,提供更简洁的 constexpr C++14 中的函数。你验证我的更改吗?干杯
          • endian.h 不是 C++ 标准头文件。当它发生时有一个endian.h文件,如果它没有定义BYTE_ORDER和__LITTLE_ENDIAN宏,那就更危险了。因为未定义的宏将被视为 0,因此它们相等。 __bswap_constant_XX 是 gcc/clang 特定的。我想使用编译器宏来获得 GCC/Clang/MSVC 的最小可移植解决方案:#ifdef __GNUC // 也适用于 clang __builtin_bswap64/32/16 #elif defined(_MSC_VER) #else _byteswap_ushort/_byteswap_ulong /_byteswap_uint64 #error 不支持 #endif
          • 非常感谢@zhaorufei :-) 感谢您的反馈,我改进了答案 :-) 请查看答案并告诉我是否可以。我还没有编译sn-ps...请同时验证sn-ps是否正确。谢谢你。保重
          【解决方案14】:

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

          #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 和 big endian 网络,但如果您使用的是 big endian 编码,那么这将不必要地反转字节顺序,因此可能需要进行一点“if __BYTE_ORDER == __LITTLE_ENDIAN”检查,以使您的代码更具可移植性,依赖根据您的需要。

          更新:编辑显示字节序检查示例

          【讨论】:

            【解决方案15】:

            任何值大小的通用函数。

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

            【讨论】:

            • 仅仅交换字节是不够的。您必须知道输入值是否在目标字节序中已经,然后仅在需要时交换字节。 hton...()ntoh...() 函数处理这种逻辑。
            【解决方案16】:

            htonl可以通过以下步骤完成

            • 如果它的大端系统直接返回值。无需进行任何转换。如果是litte endian系统,需要做如下转换。
            • 采用 LSB 32 位并应用“htonl”并移位 32 次。
            • 取 MSB 32 位(通过将 uint64_t 值向右移动 32 次)并应用 'htonl'
            • 现在对第 2 步和第 3 步中收到的值应用按位 OR。

            ntohll 也一样

            #define HTONLL(x) ((1==htonl(1)) ? (x) : (((uint64_t)htonl((x) & 0xFFFFFFFFUL)) << 32) | htonl((uint32_t)((x) >> 32)))
            #define NTOHLL(x) ((1==ntohl(1)) ? (x) : (((uint64_t)ntohl((x) & 0xFFFFFFFFUL)) << 32) | ntohl((uint32_t)((x) >> 32)))
            

            您也可以将上述 2 个定义定义为函数。

            【讨论】:

              【解决方案17】:
              union help64
              {
                  unsigned char byte[8];
                  uint64_t quad;
              };
              uint64_t ntoh64(uint64_t src)
              {
                  help64 tmp;
                  tmp.quad = src;
                  uint64_t dst = 0;
                  for(int i = 0; i < 8; ++i)
                      dst = (dst << 8) + tmp.byte[i];
                  return dst;
              }
              

              【讨论】:

                猜你喜欢
                • 2010-09-14
                • 1970-01-01
                • 2011-11-22
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多