【问题标题】:C Macro definition to determine big endian or little endian machine?C 宏定义来确定大端或小端机器?
【发布时间】:2011-01-07 04:54:56
【问题描述】:

是否有单行宏定义来确定机器的字节顺序。我正在使用以下代码,但将其转换为宏会太长。

unsigned char test_endian( void )
{
    int test_var = 1;
    unsigned char *test_endian = (unsigned char*)&test_var;

    return (test_endian[0] == 0);
}

【问题讨论】:

  • 为什么不在宏中包含相同的代码?
  • 您不能仅使用 C 预处理器就可移植地确定字节顺序。您还希望在最终测试中使用0 而不是NULL,并将test_endian 对象之一更改为其他对象:-)。
  • 还有为什么需要宏?内联函数会做同样的事情,而且更安全。
  • @Sharptooth,宏很有吸引力,因为它的值可能在编译时就知道了,这意味着您可以使用平台的字节序来控制模板实例化,或者甚至可以选择不同的代码块#if 指令。
  • 确实如此,但效率低下。如果我有一个 little-endian cpu,并且我正在将 little-endian 数据写入线路或文件,我宁愿避免无意义地解包和重新打包数据。我曾经以编写视频驱动程序为生。在将像素写入视频卡以优化每个可以优化的地方时,这一点非常

标签: c architecture macros endianness


【解决方案1】:

这个问题对 cpp 来说也是实际的,所以我在这里问了。

仅限#if __cplusplus > 201703L

#include <bit>
#include <iostream>

using namespace std;

int main()
{
    if constexpr (endian::native == endian::big)
        cout << "big-endian";
    else if constexpr (endian::native == endian::little)
        cout << "little-endian";
    else
        cout << "mixed-endian";
}

欲了解更多信息:https://en.cppreference.com/w/cpp/types/endian

【讨论】:

    【解决方案2】:

    如果您的编译器支持复合字面量,并且您明确不使用 C++,则可以使用

    #define BIG_ENDIAN      ((*(const char*)&(const int){0x01020304}) == 0x01)
    #define LITTLE_ENDIAN   ((*(const char*)&(const int){0x01020304}) == 0x04)
    

    这不需要声明任何运行时变量,我认为这使它比大多数其他解决方案更干净

    【讨论】:

      【解决方案3】:

      如果你转储预处理器#defines

      gcc -dM -E - < /dev/null
      g++ -dM -E -x c++ - < /dev/null
      

      您通常可以找到对您有帮助的东西。带有编译时逻辑。

      #define __LITTLE_ENDIAN__ 1
      #define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
      

      然而,不同的编译器可能有不同的定义。

      【讨论】:

        【解决方案4】:

        请注意,这里的大多数答案都是不可移植的,因为今天的编译器会在编译时评估这些答案(取决于优化)并根据特定的字节序返回一个特定的值,而实际的机器字节序可能会有所不同.测试字节序的值永远不会到达系统内存,因此无论实际字节序如何,实际执行的代码都将返回相同的结果。

        对于example,在 ARM Cortex-M3 中,实现的字节序将反映在状态位 AIRCR.ENDIANNESS 中,编译器在编译时无法知道该值。

        此处建议的一些答案的编译输出:

        https://godbolt.org/z/GJGNE2this 回答,

        https://godbolt.org/z/Yv-pyJthis 回答,以此类推。

        要解决它,您需要使用volatile 限定符。 Yogeesh H T 的答案是当今现实生活中最接近的答案,但由于Christoph 提出了更全面的解决方案,因此对他的answer 稍作修复将使答案完整,只需将volatile 添加到联合声明中: static const volatile union

        这将确保存储和读取内存,这是确定字节顺序所必需的。

        【讨论】:

          【解决方案5】:

          支持任意字节顺序的代码,准备放入名为order32.h的文件中:

          #ifndef ORDER32_H
          #define ORDER32_H
          
          #include <limits.h>
          #include <stdint.h>
          
          #if CHAR_BIT != 8
          #error "unsupported char size"
          #endif
          
          enum
          {
              O32_LITTLE_ENDIAN = 0x03020100ul,
              O32_BIG_ENDIAN = 0x00010203ul,
              O32_PDP_ENDIAN = 0x01000302ul,      /* DEC PDP-11 (aka ENDIAN_LITTLE_WORD) */
              O32_HONEYWELL_ENDIAN = 0x02030001ul /* Honeywell 316 (aka ENDIAN_BIG_WORD) */
          };
          
          static const union { unsigned char bytes[4]; uint32_t value; } o32_host_order =
              { { 0, 1, 2, 3 } };
          
          #define O32_HOST_ORDER (o32_host_order.value)
          
          #endif
          

          您可以通过以下方式检查小端系统

          O32_HOST_ORDER == O32_LITTLE_ENDIAN
          

          【讨论】:

          • 这不会让你在运行时之前决定 endian-ness。以下无法编译,因为。 /** isLittleEndian::result --> 0 或 1 */ struct isLittleEndian { enum isLittleEndianResult { result = (O32_HOST_ORDER == O32_LITTLE_ENDIAN) }; };
          • 直到运行时才能得到结果吗?
          • 为什么是char?最好使用uint8_t,如果此类型不可用则失败(可以通过#if UINT8_MAX 进行检查)。请注意,CHAR_BIT 独立于 uint8_t
          • 为了完整起见,让我再加入一个:O32_HONEYWELL_ENDIAN = 0x02030001ul /* Honeywell 316 */
          【解决方案6】:

          如果您的编译器支持 C99 复合文字:

          #define IS_BIG_ENDIAN (!*(unsigned char *)&(uint16_t){1})
          

          或:

          #define IS_BIG_ENDIAN (!(union { uint16_t u16; unsigned char c; }){ .u16 = 1 }.c)
          

          一般来说,您应该尝试编写不依赖于主机平台字节序的代码。


          ntohl() 的主机端独立实现示例:

          uint32_t ntohl(uint32_t n)
          {
              unsigned char *np = (unsigned char *)&n;
          
              return ((uint32_t)np[0] << 24) |
                  ((uint32_t)np[1] << 16) |
                  ((uint32_t)np[2] << 8) |
                  (uint32_t)np[3];
          }
          

          【讨论】:

          • “您应该尝试编写不依赖于主机平台字节序的代码”。不幸的是,我的请求“我知道我们正在编写一个 POSIX 兼容层,但我不想实现 ntoh,因为它取决于主机平台的字节序”总是被置若罔闻;-)。图形格式处理和转换代码是我见过的另一个主要候选代码——你不想把所有事情都建立在一直调用 ntohl 的基础上。
          • 您可以以不依赖于主机平台字节序的方式实现ntohl
          • @caf 你将如何以独立于主机端序的方式编写 ntohl?
          • @AliVeli:我在答案中添加了一个示例实现。
          • 我还应该添加记录,无论我优化多少,“(*(uint16_t *)"\0\xff"
          【解决方案7】:

          查找字节序的宏

          #define ENDIANNES() ((1 && 1 == 0) ? printf("Big-Endian"):printf("Little-Endian"))
          

          #include <stdio.h>
          
          #define ENDIAN() { \
          volatile unsigned long ul = 1;\
          volatile unsigned char *p;\
          p = (volatile unsigned char *)&ul;\
          if (*p == 1)\
          puts("Little endian.");\
          else if (*(p+(sizeof(unsigned long)-1)) == 1)\
          puts("Big endian.");\
          else puts("Unknown endian.");\
          }
          
          int main(void) 
          {
                 ENDIAN();
                 return 0;
          }
          

          【讨论】:

          • 第一个宏不正确,将始终返回“Big-Endian”。位移不受字节序影响 - 字节序仅影响对内存的读取和存储。
          【解决方案8】:

          用于检查系统是 little-endian 还是 big-indian 的 C 代码。

          int i = 7;
          char* pc = (char*)(&i);
          if (pc[0] == '\x7') // aliasing through char is ok
              puts("This system is little-endian");
          else
              puts("This system is big-endian");
          

          【讨论】:

            【解决方案9】:

            不要忘记字节序不是全部 - char 的大小可能不是 8 位(例如 DSP),不能保证二进制补码否定(例如 Cray),可能需要严格对齐(例如 SPARC , ARM 在未对齐时也会跳入 middle-endian) 等等。

            改为针对特定的CPU 架构 可能是一个更好的主意。

            例如:

            #if defined(__i386__) || defined(_M_IX86) || defined(_M_IX64)
              #define USE_LITTLE_ENDIAN_IMPL
            #endif
            
            void my_func()
            {
            #ifdef USE_LITTLE_ENDIAN_IMPL
              // Intel x86-optimized, LE implementation
            #else
              // slow but safe implementation
            #endif
            }
            

            请注意,不幸的是,此解决方案也不是超便携的,因为它取决于编译器特定的定义(没有标准,但 here's 是此类定义的一个很好的汇编)。

            【讨论】:

              【解决方案10】:

              如果您正在寻找编译时测试并且您正在使用 gcc,您可以这样做:

              #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
              

              更多信息请参见gcc documentation

              【讨论】:

              • 这绝对是任何使用 gcc 的人的最佳答案
              • __BYTE_ORDER__ 自 GCC 4.6 起可用
              【解决方案11】:

              我的回答没有按要求回答,但是您的系统是小端还是大端?

              代码:

              #include<stdio.h>
              
              int main()
              {
                int a = 1;
                char *b;
              
                b = (char *)&a;
                if (*b)
                  printf("Little Endian\n");
                else
                  printf("Big Endian\n");
              }
              

              【讨论】:

                【解决方案12】:

                “C 网络库”提供了处理字节序的函数。即 htons()、htonl()、ntohs() 和 ntohl() ...其中 n 是“网络”(即 big-endian),h 是“主机”(即运行代码)。

                这些明显的“函数”(通常)定义为宏 [参见 ],因此使用它们没有运行时开销。

                以下宏使用这些“函数”来评估字节序。

                #include <arpa/inet.h>
                #define  IS_BIG_ENDIAN     (1 == htons(1))
                #define  IS_LITTLE_ENDIAN  (!IS_BIG_ENDIAN)
                

                另外:

                我唯一需要知道一个系统的字节序的时候是当我写出一个变量[到一个文件/其他]可能被另一个未知字节序的系统读入时(对于交叉-platform compatability) ...在这些情况下,您可能更喜欢直接使用 endian 函数:

                #include <arpa/inet.h>
                
                #define JPEG_MAGIC  (('J'<<24) | ('F'<<16) | ('I'<<8) | 'F')
                
                // Result will be in 'host' byte-order
                unsigned long  jpeg_magic = JPEG_MAGIC;
                
                // Result will be in 'network' byte-order (IE. Big-Endian/Human-Readable)
                unsigned long  jpeg_magic = htonl(JPEG_MAGIC);
                

                【讨论】:

                • 这并不能真正回答正在寻找快速确定字节顺序的问题。
                • @Oren :关于您的有效批评,我在前面添加了更直接地解决原始问题的细节。
                【解决方案13】:

                实际上,您可以通过使用复合文字 (C99) 来访问临时对象的内存:

                #define IS_LITTLE_ENDIAN (1 == *(unsigned char *)&(const int){1})
                

                哪个 GCC 将在编译时评估。

                【讨论】:

                • 我喜欢它。是否有一种可移植的编译时方法来知道您是在 C99 下编译的吗?
                • 哦,如果不是 GCC 怎么办?
                • @EdwardFalk 是的。 #if __STDC_VERSION__ &gt;= 199901L.
                【解决方案14】:

                我相信这就是我们所要求的。 我只在msvc下的一个小端机器上测试了这个。 有人请在大端机器上确认。

                    #define LITTLE_ENDIAN 0x41424344UL 
                    #define BIG_ENDIAN    0x44434241UL
                    #define PDP_ENDIAN    0x42414443UL
                    #define ENDIAN_ORDER  ('ABCD') 
                
                    #if ENDIAN_ORDER==LITTLE_ENDIAN
                        #error "machine is little endian"
                    #elif ENDIAN_ORDER==BIG_ENDIAN
                        #error "machine is big endian"
                    #elif ENDIAN_ORDER==PDP_ENDIAN
                        #error "jeez, machine is PDP!"
                    #else
                        #error "What kind of hardware is this?!"
                    #endif
                

                作为旁注(特定于编译器),使用激进的编译器,您可以使用“死代码消除”优化来实现与编译时相同的效果#if,如下所示:

                    unsigned yourOwnEndianSpecific_htonl(unsigned n)
                    {
                        static unsigned long signature= 0x01020304UL; 
                        if (1 == (unsigned char&)signature) // big endian
                            return n;
                        if (2 == (unsigned char&)signature) // the PDP style
                        {
                            n = ((n << 8) & 0xFF00FF00UL) | ((n>>8) & 0x00FF00FFUL);
                            return n;
                        }
                        if (4 == (unsigned char&)signature) // little endian
                        {
                            n = (n << 16) | (n >> 16);
                            n = ((n << 8) & 0xFF00FF00UL) | ((n>>8) & 0x00FF00FFUL);
                            return n;
                        }
                        // only weird machines get here
                        return n; // ?
                    }
                

                以上依赖于编译器在编译时识别常量值的事实,完全删除if (false) { ... }内的代码并将if (true) { foo(); }之类的代码替换为foo();最坏的情况:编译器不做优化,您仍然可以得到正确的代码,但速度会慢一些。

                【讨论】:

                • 我喜欢这种方法,但如果我错了,请纠正我:这仅在您正在构建的机器上编译时有效,对吗?
                • gcc 也会由于多字符字符常量而引发错误。因此,不便携。
                • 什么编译器让你写'ABCD'
                • 许多编译器将允许在宽松的合规模式下使用多字节字符常量,但使用clang -Wpedantic -Werror -Wall -ansi foo.c 运行顶部会出错。 (特别是 Clang 和这个:-Wfour-char-constants -Werror
                • @Edward Falk 在代码中包含多字符常量不是错误。它是实现定义的行为 C11 6.4.4.4。 10. gcc 和其他可能/可能不会警告/错误取决于设置,但它不是 C 错误。使用多字符字符常量当然不流行。
                【解决方案15】:
                #include <stdint.h>
                #define IS_LITTLE_ENDIAN (*(uint16_t*)"\0\1">>8)
                #define IS_BIG_ENDIAN (*(uint16_t*)"\1\0">>8)
                

                【讨论】:

                • 这也会生成可执行代码,而不是常量。你不能做“#if IS_BIG_ENDIAN”
                • 我喜欢这个解决方案,因为据我了解,它不依赖于 C/C++ 标准的未定义行为。这不是编译时间,但唯一的标准解决方案是等待 c++20 std::endian
                【解决方案16】:

                如果你只想依赖预处理器,你必须弄清楚预定义符号的列表。预处理算术没有寻址的概念。

                GCC 在 Mac 上 定义 __LITTLE_ENDIAN____BIG_ENDIAN__

                $ gcc -E -dM - < /dev/null |grep ENDIAN
                #define __LITTLE_ENDIAN__ 1
                

                然后,您可以添加更多基于平台检测的预处理器条件指令,如#ifdef _WIN32 等。

                【讨论】:

                • Linux 上的 GCC 4.1.2 似乎没有定义这些宏,尽管 GCC 4.0.1 和 4.2.1 在 Macintosh 上定义了它们。所以它不是跨平台开发的可靠方法,即使你被允许指定使用哪个编译器。
                • 哦,是的,这是因为它仅由 Mac 上的 GCC 定义。
                • 注意:我的 GCC(在 Mac 上)定义了 #define __BIG_ENDIAN__ 1#define _BIG_ENDIAN 1
                • clang 5.0.1 for OpenBSD/amd64 有#define __LITTLE_ENDIAN__ 1。这个宏似乎是一个clang特性,而不是gcc特性。某些 Mac 中的 gcc 命令不是 gcc,而是 clang。
                • 当时 Mac 上的 GCC 4.2.1 是 GCC
                【解决方案17】:

                试试这个:

                #include<stdio.h>        
                int x=1;
                #define TEST (*(char*)&(x)==1)?printf("little endian"):printf("Big endian")
                int main()
                {
                
                   TEST;
                }
                

                【讨论】:

                  【解决方案18】:

                  虽然没有可移植的#define 或其他可依赖的东西,但平台确实提供了标准函数,用于与“主机”端进行转换。

                  通常,您使用“network endian”(BIG endian)进行存储到磁盘或网络,并使用主机端进行本地计算(在 x86 上是 LITTLE em> 字节序)。你使用htons()ntohs()和朋友在两者之间进行转换。

                  【讨论】:

                    【解决方案19】:

                    要在运行时检测字节序,您必须能够引用内存。如果你坚持使用标准 C,在内存中声明一个变量需要一个语句,但返回一个值需要一个表达式。我不知道如何在单个宏中做到这一点——这就是 gcc 有扩展的原因:-)

                    如果你愿意有一个.h文件,你可以定义

                    static uint32_t endianness = 0xdeadbeef; 
                    enum endianness { BIG, LITTLE };
                    
                    #define ENDIANNESS ( *(const char *)&endianness == 0xef ? LITTLE \
                                       : *(const char *)&endianness == 0xde ? BIG \
                                       : assert(0))
                    

                    然后您就可以随意使用ENDIANNESS 宏了。

                    【讨论】:

                    • 我喜欢这个,因为它承认除了大小之外还有字节序的存在。
                    • 说到这里,调用宏 INT_ENDIANNESS 甚至 UINT32_T_ENDIANNESS 可能是值得的,因为它只测试一种类型的存储表示。有一个 ARM ABI,其中整数类型是 little-endian,但双精度类型是 middle-endian(每个单词都是 little-endian,但其中带有符号位的单词位于另一个单词之前)。这让编译器团队兴奋了一天左右,我可以告诉你。
                    【解决方案20】:

                    没有标准,但在包括&lt;endian.h&gt; 在内的许多系统上会给您一些定义供您查找。

                    【讨论】:

                    • #if __BYTE_ORDER == __LITTLE_ENDIAN#elif __BYTE_ORDER == __BIG_ENDIAN 测试字节顺序。并生成一个#error elsewise。
                    • &lt;endian.h&gt; 在 Windows 上不可用
                    • AndroidChromium 项目使用 endian.h,除非定义了 __APPLE___WIN32
                    • 在 OpenBSD 6.3 中, 提供了#if BYTE_ORDER == LITTLE_ENDIAN(或BIG_ENDIAN),名称前没有下划线。 _BYTE_ORDER 仅适用于系统标头。 __BYTE_ORDER 不存在。
                    • @To1ne 我怀疑字节顺序是否与 Windows 相关,因为 Windows(至少目前)仅在 x86 和 ARM 机器上运行。 x86 始终是 LE,而 ARM 可配置为使用任一架构。
                    【解决方案21】:

                    使用内联函数而不是宏。此外,您需要在内存中存储一​​些东西,这是宏的一个不太好的副作用。

                    您可以使用静态或全局变量将其转换为短宏,如下所示:

                    static int s_endianess = 0;
                    #define ENDIANESS() ((s_endianess = 1), (*(unsigned char*) &s_endianess) == 0)
                    

                    【讨论】:

                    • 我认为这是最好的,因为它是最简单的。但是它不会针对混合端进行测试
                    • 为什么s_endianess 不设置为1?
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2013-04-30
                    • 1970-01-01
                    • 2013-05-17
                    • 2019-08-28
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多