【问题标题】:Detecting endianness programmatically in a C++ program在 C++ 程序中以编程方式检测字节顺序
【发布时间】:2010-11-03 07:57:54
【问题描述】:

是否有一种编程方式来检测您是在大端还是小端架构上?我需要能够编写将在 Intel 或 PPC 系统上执行的代码并使用完全相同的代码(即无条件编译)。

【问题讨论】:

标签: c++ algorithm endianness


【解决方案1】:

除非您使用已移植到 PPC 和 Intel 处理器的框架,否则您必须进行条件编译,因为 PPC 和 Intel 平台具有完全不同的硬件架构、管道、总线等。这会呈现汇编代码两者完全不同。

至于查找字节顺序,请执行以下操作:

short temp = 0x1234;
char* tempChar = (char*)&temp;

您将得到 tempChar 为 0x12 或 0x34,从中您将知道字节顺序。

【讨论】:

  • 这依赖于 short 正好是 2 个字节,这是不能保证的。
  • 尽管基于问题中给出的两种架构,但这将是一个相当安全的选择。
  • 包括 stdint.h 并使用 int16_t 来证明未来不会在另一个平台上出现不同的短路。
【解决方案2】:

声明一个int变量:

int variable = 0xFF;

现在使用 char* 指针指向它的各个部分并检查这些部分中的内容。

char* startPart = reinterpret_cast<char*>( &variable );
char* endPart = reinterpret_cast<char*>( &variable ) + sizeof( int ) - 1;

现在根据哪个指向 0xFF 字节,您可以检测字节序。这需要 sizeof( int ) > sizeof( char ),但对于所讨论的平台来说绝对是正确的。

【讨论】:

    【解决方案3】:

    请看this article:

    这里有一些代码来确定什么是 您的机器类型

    int num = 1;
    if(*(char *)&num == 1)
    {
        printf("\nLittle-Endian\n");
    }
    else
    {
        printf("Big-Endian\n");
    }
    

    【讨论】:

    • 请记住,这取决于 int 和 char 的长度不同,这几乎总是如此,但不能保证。
    • 我在嵌入式系统上工作过,其中 short int 和 char 大小相同......我不记得常规 int 是否也是那个大小(2 字节)。
    • 为什么这个答案几乎是唯一不会让我觉得“老兄,你在做什么?”的答案,这是这里大多数答案的情况:o
    • @Shillard int 必须至少那么大,但标准中没有要求将 char 限制为更少!如果您查看 TI F280x 系列,您会发现 CHAR_BIT 为 16 并且 sizeof(int) == sizeof(char) 而您提到的限制保持得很好......
    • 为什么不使用 uint8_t 和 uint16_t?
    【解决方案4】:

    参见Endianness - C 级代码说明。

    // assuming target architecture is 32-bit = 4-Bytes
    enum ENDIANNESS{ LITTLEENDIAN , BIGENDIAN , UNHANDLE };
    
    
    ENDIANNESS CheckArchEndianalityV1( void )
    {
        int Endian = 0x00000001; // assuming target architecture is 32-bit    
    
        // as Endian = 0x00000001 so MSB (Most Significant Byte) = 0x00 and LSB (Least     Significant Byte) = 0x01
        // casting down to a single byte value LSB discarding higher bytes    
    
        return (*(char *) &Endian == 0x01) ? LITTLEENDIAN : BIGENDIAN;
    } 
    

    【讨论】:

      【解决方案5】:

      您可以通过设置一个 int 并屏蔽位来做到这一点,但可能最简单的方法就是使用内置的网络字节转换操作(因为网络字节顺序总是大端)。

      if ( htonl(47) == 47 ) {
        // Big endian
      } else {
        // Little endian.
      }
      

      位摆弄可能会更快,但这种方式简单、直接且几乎不可能搞砸。

      【讨论】:

      • 网络转换操作也可用于将所有内容转换为大端,从而解决 Jay 可能遇到的其他问题。
      • @sharptooth - 慢是一个相对术语,但是是的,如果速度确实是个问题,请在程序开始时使用一次并设置一个具有字节序的全局变量。
      • htonl 还有一个问题:在某些平台上(windows ?),它不驻留在适当的 C 运行时库中,而是驻留在与网络相关的库(套接字等)中。如果您不需要该库,这对于仅一个函数来说是一个很大的障碍。
      • 请注意,在 Linux (gcc) 上,htonl 在编译时受到常量折叠的影响,因此这种形式的表达式根本没有运行时开销(即它被常量折叠为 1 或 0,然后死代码消除删除 if) 的另一个分支
      • 此外,在 x86 上,htonl 可以(并且现在在 Linux/gcc 上)使用内联汇编器非常有效地实现,特别是如果您的目标是支持 BSWAP 操作的微架构。跨度>
      【解决方案6】:
      int i=1;
      char *c=(char*)&i;
      bool littleendian=c;
      

      【讨论】:

        【解决方案7】:

        这个怎么样?

        #include <cstdio>
        
        int main()
        {
            unsigned int n = 1;
            char *p = 0;
        
            p = (char*)&n;
            if (*p == 1)
                std::printf("Little Endian\n");
            else 
                if (*(p + sizeof(int) - 1) == 1)
                    std::printf("Big Endian\n");
                else
                    std::printf("What the crap?\n");
            return 0;
        }
        

        【讨论】:

          【解决方案8】:

          有关更多详细信息,您可能需要查看此代码项目文章Basic concepts on Endianness

          如何在运行时动态测试 Endian 类型?

          如计算机中所述 动画FAQ,可以使用 下面的函数来看看你的代码 在小端或大端上运行 系统:折叠

          #define BIG_ENDIAN      0
          #define LITTLE_ENDIAN   1
          
          int TestByteOrder()
          {
             short int word = 0x0001;
             char *byte = (char *) &word;
             return(byte[0] ? LITTLE_ENDIAN : BIG_ENDIAN);
          }
          

          此代码将值 0001h 分配给 16 位整数。然后是一个 char 指针 分配到第一个 (最低有效)字节的 整数值。如果第一个字节 整数为0x01h,则系统 是 Little-Endian(0x01h 在 最低的或最不重要的, 地址)。如果是 0x00h 那么 系统是大端的。

          【讨论】:

            【解决方案9】:

            我会这样做:

            bool isBigEndian() {
                static unsigned long x(1);
                static bool result(reinterpret_cast<unsigned char*>(&x)[0] == 0);
                return result;
            }
            

            按照这些思路,您将获得一个只执行一次计算的高效函数。

            【讨论】:

            • 可以内联吗?不确定 inline 是否会导致静态变量的多个内存块
            【解决方案10】:

            我不喜欢基于类型双关语的方法 - 编译器经常会警告它。这正是工会的用途!

            bool is_big_endian(void)
            {
                union {
                    uint32_t i;
                    char c[4];
                } bint = {0x01020304};
            
                return bint.c[0] == 1; 
            }
            

            原理等同于其他人建议的类型案例,但这更清楚-根据C99,保证是正确的。与直接指针转换相比,gcc 更喜欢这种方式。

            这也比在编译时修复字节序要好得多 - 对于支持多架构的操作系统(例如 Mac os x 上的胖二进制文件),这对 ppc/i386 都适用,但很容易搞砸否则事情会发生。

            【讨论】:

            • 我不建议将变量命名为“bint”:)
            • 你确定这是明确定义的吗?在 C++ 中,一次只能激活一个联合成员 - 即您不能使用一个成员名称进行分配并使用另一个成员名称进行读取(尽管布局兼容结构有一个例外)
            • @Matt:我查看了谷歌,bint 似乎在英语中有一个我不知道的含义:)
            • 我已经对此进行了测试,在 gcc 4.0.1 和 gcc 4.4.1 中,此函数的结果可以在编译时确定并视为常量。这意味着如果分支仅取决于此函数的结果并且永远不会在相关平台上采用,则编译器将丢弃。许多 htonl 实现可能并非如此。
            • 这个解决方案真的便携吗?如果CHAR_BIT != 8 怎么办?
            【解决方案11】:

            这通常在编译时完成(特别是出于性能原因),方法是使用编译器提供的头文件或创建您自己的头文件。在 linux 上,你有头文件“/usr/include/endian.h”

            【讨论】:

            • 我不敢相信这没有被投票得更高。这不像是在编译的程序下字节序会改变,所以永远不需要运行时测试。
            • @Dolda2000 有可能,看看 ARM 字节序模式。
            • @Tyzoid:不,编译后的程序将始终在编译时使用的字节序模式下运行,即使处理器能够做到这一点。
            【解决方案12】:

            您也可以通过预处理器使用诸如 boost 头文件之类的东西来执行此操作,该文件可以在 boost endian 找到

            【讨论】:

              【解决方案13】:

              这是另一个 C 版本。它定义了一个名为 wicked_cast() 的宏,用于通过 C99 联合文字和非标准 __typeof__ 运算符进行内联类型双关语。

              #include <limits.h>
              
              #if UCHAR_MAX == UINT_MAX
              #error endianness irrelevant as sizeof(int) == 1
              #endif
              
              #define wicked_cast(TYPE, VALUE) \
                  (((union { __typeof__(VALUE) src; TYPE dest; }){ .src = VALUE }).dest)
              
              _Bool is_little_endian(void)
              {
                  return wicked_cast(unsigned char, 1u);
              }
              

              如果整数是单字节值,字节序没有意义,会产生编译时错误。

              【讨论】:

                【解决方案14】:

                我很惊讶没有人提到预处理器默认定义的宏。虽然这些会因您的平台而异;它们比编写自己的字节序检查要干净得多。

                例如;如果我们查看 GCC 定义的内置宏(在 X86-64 机器上):

                :| gcc -dM -E -x c - |grep -i endian
                #define __LITTLE_ENDIAN__ 1
                

                在 PPC 机器上我得到:

                :| gcc -dM -E -x c - |grep -i endian
                #define __BIG_ENDIAN__ 1
                #define _BIG_ENDIAN 1
                

                :| gcc -dM -E -x c - 魔法打印出所有内置宏)。

                【讨论】:

                • 这些宏根本不会始终如一地出现。例如,在来自 Redhat 6 存储库的 gcc 4.4.5 中,运行 echo "\n" | gcc -x c -E -dM - |&amp; grep -i 'endian' 不会返回任何内容,而 Solaris 中的 gcc 3.4.3(无论如何来自 /usr/sfw/bin)具有这些方面的定义。我在 VxWorks Tornado (gcc 2.95) -vs- VxWorks Workbench (gcc 3.4.4) 上看到过类似的问题。
                【解决方案15】:

                Ehm... 令我惊讶的是,没有人意识到编译器会简单地优化测试,并将固定结果作为返回值。这使得上面的所有代码示例都变得毫无用处。唯一会返回的是编译时的字节序!是的,我测试了上述所有示例。这是一个使用 MSVC 9.0 (Visual Studio 2008) 的示例。

                纯 C 代码

                int32 DNA_GetEndianness(void)
                {
                    union 
                    {
                        uint8  c[4];
                        uint32 i;
                    } u;
                
                    u.i = 0x01020304;
                
                    if (0x04 == u.c[0])
                        return DNA_ENDIAN_LITTLE;
                    else if (0x01 == u.c[0])
                        return DNA_ENDIAN_BIG;
                    else
                        return DNA_ENDIAN_UNKNOWN;
                }
                

                反汇编

                PUBLIC  _DNA_GetEndianness
                ; Function compile flags: /Ogtpy
                ; File c:\development\dna\source\libraries\dna\endian.c
                ;   COMDAT _DNA_GetEndianness
                _TEXT   SEGMENT
                _DNA_GetEndianness PROC                 ; COMDAT
                
                ; 11   :     union 
                ; 12   :     {
                ; 13   :         uint8  c[4];
                ; 14   :         uint32 i;
                ; 15   :     } u;
                ; 16   : 
                ; 17   :     u.i = 1;
                ; 18   : 
                ; 19   :     if (1 == u.c[0])
                ; 20   :         return DNA_ENDIAN_LITTLE;
                
                    mov eax, 1
                
                ; 21   :     else if (1 == u.c[3])
                ; 22   :         return DNA_ENDIAN_BIG;
                ; 23   :     else
                ; 24   :        return DNA_ENDIAN_UNKNOWN;
                ; 25   : }
                
                    ret
                _DNA_GetEndianness ENDP
                END
                

                也许可以只为这个函数关闭任何编译时优化,但我不知道。否则,也许可以在汇编中对其进行硬编码,尽管这不是可移植的。即便如此,即使这样也可能会得到优化。这让我觉得我需要一些非常糟糕的汇编程序,为所有现有的 CPU/指令集实现相同的代码,而且……没关系。

                另外,这里有人说字节序在运行时不会改变。错误的。那里有双端机器。它们的字节顺序在执行过程中会有所不同。此外,不仅有 Little Endian 和 Big Endian,还有其他 endiannesses(真是一个词)。

                我既讨厌又喜欢编码......

                【讨论】:

                • 你不需要重新编译才能在不同的平台上运行吗?
                • 虽然它适用于 MSVC,但并非在所有情况下都适用于所有 GCC 版本。因此,关键循环内的“运行时检查”可能在编译时正确地取消分支,也可能不正确。没有 100% 的保证。
                • 没有大端的 x86 处理器。即使您在双端处理器(如 ARM 或 MIPS)上运行 Ubuntu,ELF 可执行文件也始终是大端(MSB)或小端(LSB)。不能创建 biendian 可执行文件,因此不需要运行时检查。
                • 要关闭此方法中的优化使用'volatile union ...'它告诉编译器'u'可以在其他地方更改并且应该加载数据
                • 对于这个函数在运行时返回一个与优化器计算的不同的值,这意味着优化器被窃听。您是说编译后的优化二进制代码的示例可以在两种不同字节序的不同架构上可移植地运行,尽管优化器(在整个程序中)在编译期间做出了明显的假设,这些假设似乎与其中至少一个不兼容架构?
                【解决方案16】:
                union {
                    int i;
                    char c[sizeof(int)];
                } x;
                x.i = 1;
                if(x.c[0] == 1)
                    printf("little-endian\n");
                else    printf("big-endian\n");
                

                这是另一种解决方案。类似于 Andrew Hare 的解决方案。

                【讨论】:

                  【解决方案17】:

                  如上所述,使用联合技巧。

                  不过,上面建议的方法几乎没有问题,最值得注意的是,对于大多数架构而言,未对齐的内存访问速度非常慢,一些编译器甚至根本无法识别此类常量谓词,除非字对齐。

                  因为单纯的字节序测试很无聊,所以这里有(模板)函数,它将根据您的规范翻转任意整数的输入/输出,而不考虑主机架构。

                  #include <stdint.h>
                  
                  #define BIG_ENDIAN 1
                  #define LITTLE_ENDIAN 0
                  
                  template <typename T>
                  T endian(T w, uint32_t endian)
                  {
                      // this gets optimized out into if (endian == host_endian) return w;
                      union { uint64_t quad; uint32_t islittle; } t;
                      t.quad = 1;
                      if (t.islittle ^ endian) return w;
                      T r = 0;
                  
                      // decent compilers will unroll this (gcc)
                      // or even convert straight into single bswap (clang)
                      for (int i = 0; i < sizeof(r); i++) {
                          r <<= 8;
                          r |= w & 0xff;
                          w >>= 8;
                      }
                      return r;
                  };
                  

                  用法:

                  要将给定的字节序转换为主机,请使用:

                  host = endian(source, endian_of_source)

                  要将主机字节序转换为给定字节序,请使用:

                  output = endian(hostsource, endian_you_want_to_output)

                  生成的代码与在 clang 上编写手工汇编一样快,在 gcc 上它稍微慢一些(展开 &,>,| 对于每个字节)但仍然不错。

                  【讨论】:

                    【解决方案18】:

                    C 编译器(至少我认识的每个人)工作字节序的方式必须在编译时确定。即使对于双字节序处理器(如 ARM och MIPS),您也必须在编译时选择字节序。此外,可执行文件(例如 ELF)的所有常见文件格式都定义了字节序。虽然可以制作一个二进制 blob 的 biandian 代码(对于某些 ARM 服务器漏洞可能?),但可能必须在汇编中完成。

                    【讨论】:

                      【解决方案19】:
                      bool isBigEndian()
                      {
                          static const uint16_t m_endianCheck(0x00ff);
                          return ( *((uint8_t*)&m_endianCheck) == 0x0); 
                      }
                      

                      【讨论】:

                      • 这是等价的吗? #define IS_BIGENDIAN() (*((char*) &amp;((int){ 0x00ff })) == (0x00))
                      【解决方案20】:

                      我在看教科书:计算机系统:程序员的视角,用C程序判断这是哪个字节序有问题。

                      我使用指针的特性来做到这一点,如下所示:

                      #include <stdio.h>
                      
                      int main(void){
                          int i=1;
                          unsigned char* ii = &i;
                      
                          printf("This computer is %s endian.\n", ((ii[0]==1) ? "little" : "big"));
                          return 0;
                      }
                      

                      由于int占4个字节,而char只占1个字节。我们可以使用 char 指针 指向值为 1 的 int。因此,如果计算机是 little endian,则 char char指针指向is的值为1,否则为0。

                      【讨论】:

                      • 这将通过使用 int32t 得到改善。
                      • ^ 如果你想挑剔,这里最好的是 int16_fast_t 。并且@Archimedes520 的当前代码不适用于 int 本机为 int8 的拱门;)(不过,这可能首先违反 c 标准)
                      【解决方案21】:

                      声明:

                      我最初的帖子被错误地声明为“编译时间”。不是,在当前的 C++ 标准中甚至是不可能的。 constexpr 并不意味着函数总是进行编译时计算。感谢 Richard Hodges 的更正。

                      编译时,非宏,C++11 constexpr 解决方案:

                      union {
                        uint16_t s;
                        unsigned char c[2];
                      } constexpr static  d {1};
                      
                      constexpr bool is_little_endian() {
                        return d.c[0] == 1;
                      }
                      

                      【讨论】:

                      • 您在 uint8_t 上使用 unsigned char 是否有特殊原因?
                      • 0 运行时开销...我喜欢它!
                      • 我猜,这会检测构建机器的字节序,而不是目标?
                      • 这不是 C++ 中的 UB 吗?
                      • 这在 constexpr 上下文中是不合法的。您无法访问尚未直接初始化的联合成员。如果没有预处理器魔法,就无法在编译时合法地检测字节序。
                      【解决方案22】:

                      正如 Coriiander 所指出的,这里的大部分(如果不是全部)代码将在编译时进行优化,因此生成的二进制文件不会在运行时检查“字节顺序”。

                      据观察,给定的可执行文件不应以两种不同的字节顺序运行,但我不知道是否总是如此,而且在编译时检查这对我来说似乎是一种黑客行为。所以我编写了这个函数:

                      #include <stdint.h>
                      
                      int* _BE = 0;
                      
                      int is_big_endian() {
                          if (_BE == 0) {
                              uint16_t* teste = (uint16_t*)malloc(4);
                              *teste = (*teste & 0x01FE) | 0x0100;
                              uint8_t teste2 = ((uint8_t*) teste)[0];
                              free(teste);
                              _BE = (int*)malloc(sizeof(int));
                              *_BE = (0x01 == teste2);
                          }
                          return *_BE;
                      }
                      

                      MinGW 无法优化此代码,即使它确实优化了此处的其他代码。我相信这是因为我将分配在较小字节内存上的“随机”值保持原样(至少 7 个位),所以编译器无法知道该随机值是什么并且它不会优化函数。

                      我还对函数进行了编码,以便只执行一次检查,并为下一次测试存储返回值。

                      【讨论】:

                      • 为什么要分配 4 个字节来处理一个 2 字节的值?为什么要用0x7FE 屏蔽一个不确定的值?为什么要使用malloc()?那是浪费。而_BE 是(尽管很小)内存泄漏和等待发生的竞争条件,动态缓存结果的好处不值得麻烦。我会做更多这样的事情:static const uint16_t teste = 1; int is_little_endian() { return (0x01 == ((uint8_t*)&amp;teste)[0]); } int is_big_endian() { return (0x01 == ((uint8_t*)&amp;teste)[1]); } 简单而有效,并且在运行时执行的工作要少得多。
                      • @RemyLebeau,我回答的重点是生成一个未被编译器优化的代码。当然,您的代码要简单得多,但是打开优化后,它会在编译后变成一个常量布尔值。正如我在回答中所说,我实际上不知道是否有某种方法可以以相同的可执行文件在两个字节顺序上运行的方式编译 C 代码,而且我也很想知道是否可以在运行时进行检查尽管正在进行优化。
                      • @TexKiller 那么为什么不简单地禁用代码优化呢?使用volatile,或#pragma
                      • @RemyLebeau,当时我不知道那些关键字,我只是把它当作一个小挑战来阻止编译器优化我所知道的。
                      【解决方案23】:

                      未经测试,但在我看来,这应该有效吗?因为它会是小端的 0x01 和大端的 0x00?

                      bool runtimeIsLittleEndian(void)
                      {
                       volatile uint16_t i=1;
                       return  ((uint8_t*)&i)[0]==0x01;//0x01=little, 0x00=big
                      }
                      

                      【讨论】:

                        【解决方案24】:

                        除非 endian 标头是 GCC-only,否则它提供了您可以使用的宏。

                        #include "endian.h"
                        ...
                        if (__BYTE_ORDER == __LITTLE_ENDIAN) { ... }
                        else if (__BYTE_ORDER == __BIG_ENDIAN) { ... }
                        else { throw std::runtime_error("Sorry, this version does not support PDP Endian!");
                        ...
                        

                        【讨论】:

                        • 这些不是__BYTE_ORDER____ORDER_LITTLE_ENDIAN____ORDER_BIG_ENDIAN__吗?
                        • @Xeverous:在我目前的平台Android NDK上,答案中的名字是正确的。
                        【解决方案25】:

                        C++ 方法一直是使用boost,其中预处理器检查和强制转换在经过彻底测试的库中分隔开来。

                        Predef 库 (boost/predef.h) 识别 four different kinds of endianness

                        Endian Library 计划提交给 C++ 标准,并支持对字节序敏感数据的各种操作。

                        如上面的答案所述,字节顺序将成为 c++20 的一部分。

                        【讨论】:

                        • 仅供参考,“四种不同的字节序”链接已损坏,
                        • 修复并制作了 wiki
                        【解决方案26】:

                        如果您可以访问 C++20 编译器(例如 GCC 8+ 或 Clang 7+),则可以使用 std::endian

                        注意:std::endian 始于 &lt;type_traits&gt;,但在 2019 年科隆会议上,was moved&lt;bit&gt;。 GCC 8、Clang 7、8 和 9 包含在 &lt;type_traits&gt; 中,而 GCC 9+ 和 Clang 10+ 包含在 &lt;bit&gt; 中。

                        #include <bit>
                        
                        if constexpr (std::endian::native == std::endian::big)
                        {
                            // Big endian system
                        }
                        else if constexpr (std::endian::native == std::endian::little)
                        {
                            // Little endian system
                        }
                        else
                        {
                            // Something else
                        }
                        

                        【讨论】:

                        • 作为每个人,我都可以访问 C++17 和 20 个草案/提案,但是,到目前为止,是否存在任何 C++20 编译器?
                        • @Xeverous 它只需要范围枚举,所以我怀疑大多数供应商会将其添加到他们的 stdlib 实现中,作为他们早期的更改之一。
                        • @Xeverous GCC 8 已发布并支持它。
                        • 在该问题的 30 多个答案中,这似乎是唯一一个完全准确的答案(另一个答案至少部分正确)。
                        【解决方案27】:

                        如果您不想要条件编译,您可以编写与字节序无关的代码。这是一个例子(取自Rob Pike):

                        以独立于字节序的方式读取存储在磁盘上的小端整数:

                        i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
                        

                        相同的代码,尝试考虑机器字节序:

                        i = *((int*)data);
                        #ifdef BIG_ENDIAN
                        /* swap the bytes */
                        i = ((i&0xFF)<<24) | (((i>>8)&0xFF)<<16) | (((i>>16)&0xFF)<<8) | (((i>>24)&0xFF)<<0);
                        #endif
                        

                        【讨论】:

                        • @MaksymGanenko 我没有得到你的评论。是讽刺吗?我建议不要指定序列化数据的字节顺序。我建议不要编写依赖于接收数据的机器的字节序的代码。
                        • @MaksymGanenko 如果您投反对票,您可以解释为什么答案是错误的。至少要帮助潜在读者理解为什么他们不应该听我的回答。
                        【解决方案28】:

                        虽然没有快速和标准的方法来确定它,但它会输出它:

                        #include <stdio.h> 
                        int main()  
                        { 
                           unsigned int i = 1; 
                           char *c = (char*)&i; 
                           if (*c)     
                               printf("Little endian"); 
                           else
                               printf("Big endian"); 
                           getchar(); 
                           return 0; 
                        } 
                        

                        【讨论】:

                          【解决方案29】:

                          不要使用union

                          C++ 不允许通过unions 进行类型双关语!
                          从不是最后写入的字段的联合字段中读取是未定义的行为
                          许多编译器支持这样做作为扩展,但语言不保证。

                          查看此答案了解更多详情:

                          https://stackoverflow.com/a/11996970


                          只有两个有效的答案可以保证是可移植的。

                          第一个答案,如果你可以访问支持 C++20 的系统,
                          是使用 &lt;type_traits&gt; 标头中的 std::endian

                          (在撰写本文时,C++20 尚未发布,但除非发生某些事情会影响 std::endian 的包含,否则这将是在 C++ 编译时测试字节顺序的首选方法20 起。)

                          C++20 以后

                          constexpr bool is_little_endian = (std::endian::native == std::endian::little);
                          

                          在 C++20 之前,唯一有效的答案是存储一个整数,然后通过类型双关语检查它的第一个字节。
                          与使用 unions 不同,这是 C++ 的类型系统明确允许的。

                          同样重要的是要记住,为了获得最佳便携性,应该使用static_cast
                          因为reinterpret_cast 是实现定义的。

                          如果程序尝试通过非以下类型之一的左值访问对象的存储值,则行为未定义: ... charunsigned char 类型。

                          C++11 以后

                          enum class endianness
                          {
                              little = 0,
                              big = 1,
                          };
                          
                          inline endianness get_system_endianness()
                          {
                              const int value { 0x01 };
                              const void * address = static_cast<const void *>(&value);
                              const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
                              return (*least_significant_address == 0x01) ? endianness::little : endianness::big;
                          }
                          

                          C++11 以后(无枚举)

                          inline bool is_system_little_endian()
                          {
                              const int value { 0x01 };
                              const void * address = static_cast<const void *>(&value);
                              const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
                              return (*least_significant_address == 0x01);
                          }
                          

                          C++98/C++03

                          inline bool is_system_little_endian()
                          {
                              const int value = 0x01;
                              const void * address = static_cast<const void *>(&value);
                              const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
                              return (*least_significant_address == 0x01);
                          }
                          

                          【讨论】:

                          • 很确定你的代码在目标上会失败sizeof (int) == 1,这至少在过去是 C++ 允许的......:D 并不是你需要在那里进行字节序检查。
                          • "从不是最后写入的字段的联合字段中读取是未定义的行为!" 除了常见的初始序列。
                          猜你喜欢
                          • 1970-01-01
                          • 2011-09-25
                          • 1970-01-01
                          • 2012-02-17
                          • 2018-09-17
                          • 2017-02-07
                          • 2019-10-16
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多