在编译时确定字节序

59 c endianness

是否有一种安全,可移植的方法来确定(在编译期间)我的程序正在编译的平台的字节顺序?我在用C写作

[编辑]感谢您的回答,我决定坚持运行时解决方案!

Ada*_*eld 34

为了回答编译时检查的原始问题,没有标准化的方法可以在所有现有和所有未来的编译器中使用,因为现有的C,C++和POSIX标准都没有定义用于检测字节序的宏.

但是,如果您愿意将自己局限于某些已知的编译器,您可以查找每个编译器的文档,找出它们用于定义字节序的预定义宏(如果有的话). 这个页面列出了你可以寻找的几个宏,所以这里有一些代码适用于那些:

#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \
    defined(__BIG_ENDIAN__) || \
    defined(__ARMEB__) || \
    defined(__THUMBEB__) || \
    defined(__AARCH64EB__) || \
    defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__)
// It's a big-endian target architecture
#elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \
    defined(__LITTLE_ENDIAN__) || \
    defined(__ARMEL__) || \
    defined(__THUMBEL__) || \
    defined(__AARCH64EL__) || \
    defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__)
// It's a little-endian target architecture
#else
#error "I don't know what architecture this is!"
#endif
Run Code Online (Sandbox Code Playgroud)

如果您无法从其文档中找到编译器使用的预定义宏,您还可以尝试强制它来吐出其预定义宏的完整列表,并从那里猜测将起作用(使用ENDIAN,ORDER或处理器查找任何内容)它的架构名称). 此页面列出了在不同编译器中执行此操作的许多方法:

Compiler                   C macros                         C++ macros
Clang/LLVM                 clang -dM -E -x c /dev/null      clang++ -dM -E -x c++ /dev/null
GNU GCC/G++                gcc   -dM -E -x c /dev/null      g++     -dM -E -x c++ /dev/null
Hewlett-Packard C/aC++     cc    -dM -E -x c /dev/null      aCC     -dM -E -x c++ /dev/null
IBM XL C/C++               xlc   -qshowmacros -E /dev/null  xlc++   -qshowmacros -E /dev/null
Intel ICC/ICPC             icc   -dM -E -x c /dev/null      icpc    -dM -E -x c++ /dev/null
Microsoft Visual Studio (none)                              (none)
Oracle Solaris Studio      cc    -xdumpmacros -E /dev/null  CC      -xdumpmacros -E /dev/null
Portland Group PGCC/PGCPP  pgcc  -dM -E                     (none)
Run Code Online (Sandbox Code Playgroud)

最后,为了完善它,Microsoft Visual C/C++编译器是奇怪的,并没有上述任何一个.幸运的是,他们在此处记录了他们的预定义宏,您可以使用目标处理器体系结构来推断字节序.尽管所有在Windows中目前支持的处理器是小端(_M_IX86,_M_X64,_M_IA64,和_M_ARM是小端),喜欢的PowerPC(历史上的一些支持的处理器_M_PPC)为大端.但更相关的是,Xbox 360是一款大端的PowerPC机器,所以如果你正在编写一个跨平台的库头,那么检查就不会有什么坏处_M_PPC.

  • 我不熟悉Microsoft的编译器,但ARM可能以两种字节序模式运行。我不确定是否可以在编译时检查它。 (2认同)

wkl*_*wkl 32

这是用于编译时检查

您可以使用boost头文件中的信息,该文件endian.hpp涵盖许多平台.

编辑运行时检查

bool isLittleEndian()
{
    short int number = 0x1;
    char *numPtr = (char*)&number;
    return (numPtr[0] == 1);
}
Run Code Online (Sandbox Code Playgroud)

创建一个整数,并读取其第一个字节(最低有效字节).如果该字节为1,则系统为小端,否则为大端.

编辑思考它

是的,你可能会遇到某些平台(无法想到)的潜在问题sizeof(char) == sizeof(short int).您可以使用固定宽度的多字节整数类型<stdint.h>,或者如果您的平台没有它,您可以再次调整一个boost标头供您使用:stdint.hpp

  • 这不回答问题; 它是运行时检查,而不是编译时检查. (48认同)
  • *点头*.顺便说一句,如果`sizeof(char)== sizeof(short)`,那么`uint8_t`就不能存在于实现上.C99要求`uint8_t`没有填充并且正好是8位,并且还用`char`/bytes定义类型的表示,所以`uint8_t`只能在`CHAR_BIT == 8`时存在.但是,"短"不能保持所需的最小范围.:-) (3认同)

R..*_*R.. 16

使用C99,您可以执行以下检查:

#define I_AM_LITTLE (((union { unsigned x; unsigned char c; }){1}).c)
Run Code Online (Sandbox Code Playgroud)

类似的条件if (I_AM_LITTLE)将在编译时进行评估,并允许编译器优化整个块.

关于这是否严格地说是C99中的常量表达式(这将允许它在静态存储持续时间数据的初始化器中使用),我没有正确的引用,但如果没有,它是下一个最好的事情.

  • 不,它不是,甚至当你为它提供一个`const`时. (7认同)
  • @einpoklum:联合的大小是最大的类型,在本例中为 unsigned int (通常为 4 个字节),因此初始化本质上是“unsigned int x = 1;”。使用“c”字段可以获取第一个 sizeof(unsigned char) 字节,或者本质上是“x &amp; 0xff000000”。(至少,我认为它是这样工作的,我找不到这样的文档。)因此,如果是大尾数,“x”将是 00 00 00 01 和 (x &amp; 0xff000000) == 0 == false。如果是小尾数,“x”将为 01 00 00 00,因此 (x &amp; 0xff000000) == 1 == true。 (2认同)

Dan*_*uţă 10

C FAQ中有趣的读物 :

你可能不会.检测字节序的常用技术涉及char或指针的指针或数组,但预处理器算法仅使用长整数,并且没有寻址的概念.另一种诱人的可能性就像是

  #if 'ABCD' == 0x41424344
Run Code Online (Sandbox Code Playgroud)

但这也不可靠.

  • 为什么这不可靠?多字符常量确实是**唯一**有效的方式(在 C++ 中,C99 允许联合)。结果当然是 _implementation defined_,但这就是它必须是什么,因为字节顺序(这正是您想要弄清楚的!)是实现定义的。所有其他方法要么是 _undefined 行为_(联合、类型双关指针等),要么是一团糟(使用 Boost,这只不过是通过 #ifdef 和手动编码的 #define 进行平台检测)。 (5认同)

tow*_*owi 7

我想扩展constexpr为C++ 提供函数的答案

union Mix {
    int sdat;
    char cdat[4];
};
static constexpr Mix mix { 0x1 };
constexpr bool isLittleEndian() {
    return mix.cdat[0] == 1;
}
Run Code Online (Sandbox Code Playgroud)

既然mixconstexpr太是编译时间,可以在使用constexpr bool isLittleEndian().应该是安全的使用.

更新

正如@Cheersandhth在下面指出的那样,这些似乎是有问题的.

原因是,它不符合C++ 11-Standard,禁止类型惩罚.一次只能有一个工会成员处于活动状态.使用标准的符合标准的编译器,您将收到错误.

所以,不要在C++中使用它.看来,你可以在C中做到这一点.我把答案留给教育目的:-)因为问题是关于C ...

更新2

这假定int大小为4 char秒,并不总是按照下面正确指出的@PetrVepřek给出.为了使您的代码真正可移植,您必须在这里更聪明.这应该足以满足许多情况.请注意,根据定义,sizeof(char)始终是这样1.上面的代码假定sizeof(int)==4.

  • 是不是所有这些类型 - 惩罚技术都实现了定义的行为(甚至是未定义的行为)?我目前不知道实现读取联盟meber与最后写的那个真正失败的实现,但我认为严格来说它是不可移植的. (3认同)
  • ** – 1 **无法使用clang编译(这很聪明,几周前,我认为它可以工作,然后使用clang进行测试并学习了)。 (2认同)

Lik*_*ike 7

使用 CMake TestBigEndian作为

INCLUDE(TestBigEndian)
TEST_BIG_ENDIAN(ENDIAN)
IF (ENDIAN)
    # big endian
ELSE (ENDIAN)
    # little endian
ENDIF (ENDIAN)
Run Code Online (Sandbox Code Playgroud)


pr1*_*268 6

不是在编译期间,而是在运行时期间.这是我写的一个C函数来确定字节序:

/*  Returns 1 if LITTLE-ENDIAN or 0 if BIG-ENDIAN  */
#include <inttypes.h>
int endianness()
{
  union { uint8_t c[4]; uint32_t i; } data;
  data.i = 0x12345678;
  return (data.c[0] == 0x78);
}
Run Code Online (Sandbox Code Playgroud)

  • 调用UB,您只能读取写入的最后一个union成员. (2认同)

小智 5

最后,在C预处理单行字节序检测

#include <stdint.h>

#define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100)
Run Code Online (Sandbox Code Playgroud)

任何体面的优化器都会在编译时解决这个问题。gcc 在-O1.

当然stdint.h是C99。有关 ANSI/C89 可移植性,请参阅 Doug Gwyn 的Instant C9x库。