在编译时确定字节顺序
有没有一种安全,便携的方式来确定(在编译期间)我的程序正在编译的平台的字节序? 我正在写C
[编辑]感谢您的答案,我决定坚持与运行时解决scheme!
这是编译时检查
你可以使用来自boost头文件endian.hpp
,它涵盖了很多平台。
编辑运行时检查
bool isLittleEndian() { short int number = 0x1; char *numPtr = (char*)&number; return (numPtr[0] == 1); }
创build一个整数,并读取其第一个字节(最低有效字节)。 如果这个字节是1,那么系统是小端,否则是大端。
编辑思考一下
是的,你可能会遇到一些潜在的问题在一些平台(不能想到任何) sizeof(char) == sizeof(short int)
。 您可以使用<stdint.h>
提供的固定宽度的多字节整型types,或者如果您的平台没有它,您可以再次使用boost头文件stdint.hpp
为了回答编译时检查的原始问题,没有一种标准的方法可以在所有现有和将来的编译器中使用,因为现有的C,C ++和POSIX标准都没有定义用于检测字节序的macros。
但是,如果你愿意把自己限制在一些已知的编译器上,你可以查看每个编译器的文档来找出哪些预定义的macros(如果有)用来定义字节序。 这个页面列出了你可以查找的几个macros,所以下面是一些适用于这些macros的代码:
#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
如果你不能find你的编译器在其文档中使用的预定义的macros,你也可以试着强制它把它的预定义的macros全部列出来,然后从那里猜测什么可以工作(用ENDIAN,ORDER或者处理器架构名称)。 本页列出了在不同编译器中执行该操作的一些方法:
Compiler C macros C++ macros Clang/LLVM clang -dM -E -xc /dev/null clang++ -dM -E -x c++ /dev/null GNU GCC/G++ gcc -dM -E -xc /dev/null g++ -dM -E -x c++ /dev/null Hewlett-Packard C/aC++ cc -dM -E -xc /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 -xc /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)
最后,为了解决这个问题,Microsoft Visual C / C ++编译器是非常奇怪的,并且没有任何上述内容。 幸运的是,他们在这里logging了预定义的macros,并且可以使用目标处理器体系结构来推断字节序。 尽pipeWindows中所有当前支持的处理器都是小端( _M_IX86
, _M_X64
, _M_IA64
和_M_ARM
都是小端),但PowerPC( _M_PPC
)等历史上支持的处理器_M_PPC
是大端的。 但更重要的是,Xbox 360是一个大型的PowerPC机器,所以如果你正在编写一个跨平台的库头文件,检查_M_PPC
。
使用C99,您可以执行检查:
#define I_AM_LITTLE (((union { unsigned x; unsigned char c; }){1}).c)
像if (I_AM_LITTLE)
这样的条件将在编译时进行评估,并允许编译器优化整个块。
我没有参考,因为这是否严格地说是C99中的一个常量expression式 (这将允许它在静态存储持续时间数据的初始化程序中使用),但是如果不是这样的话,那么这是第二好的。
有趣的从C FAQ中读取:
你可能不能。 检测字节顺序的常用技术包括字符指针或字符数组,或可能是联合,但预处理器算术只使用长整数,并没有寻址的概念。 另一个诱人的可能性是类似的
#if 'ABCD' == 0x41424344
但是这也是不可靠的。
我想扩展为C ++提供constexpr
函数的答案
union Mix { int sdat; char cdat[4]; }; static constexpr Mix mix { 0x1 }; constexpr bool isLittleEndian() { return mix.cdat[0] == 1; }
既然mix
也是constexpr
所以编译的时候可以用在constexpr bool isLittleEndian()
。 应该安全使用。
更新
正如@Cheersandhth在下面指出的,这似乎是有问题的。
原因是,它不是C ++ 11-Standard符合 ,禁止types双关 。 一次只能有一个工会会员。 使用标准的符合编译器,你会得到一个错误。
所以, 不要在C ++中使用它 。 看来,你可以用C做。 我留下我的答案为教育目的:-)因为问题是关于C …
不是在编译期间,而是在运行时。 下面是我写的用来确定字节顺序的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); }
我曾经使用过这样一个构造:
uint16_t HI_BYTE = 0, LO_BYTE = 1; uint16_t s = 1; if(*(uint8_t *) &s == 1) { HI_BYTE = 1; LO_BYTE = 0; } pByte[HI_BYTE] = 0x10; pByte[LO_BYTE] = 0x20;
与-O2的gcc能够使它完全编译时间。 这意味着, HI_BYTE
和LO_BYTE
variables被完全replace,甚至在汇编程序中,pByte acces被相当于*(unit16_t *pByte) = 0x1020;
。
就像编译时间一样。
据我所知不,不是在编译期间。
在运行时,你可以做一些简单的检查,比如将一个多字节值设置为一个已知的位串,然后检查结果是什么字节。例如使用联合,
typedef union { uint32_t word; uint8_t bytes[4]; } byte_check;
或铸造,
uint32_t word; uint8_t * bytes = &word;
请注意,对于完全便携式的sorting检查,您需要考虑大端,小端和混端系统。
编辑2:此方法不起作用。 多字节常量的表示是编译器/平台特定的,不能可靠地使用。 链接interjay给( http://www.ideone.com/LaKpj )举例说明它失败了。 在Solaris / SPARC上,相同的编译器gcc 4.3.3给出了正确的答案,但是SUNStudio 12编译器将具有与该链接上使用的x86上的gcc 4.3.4相同的行为。
所以,我们可以得出结论,仍然没有很好的使用多字节字符
发现这个新的方法具有简单和编译时间的优点。
switch('AB') { case 0x4142: printf("ASCII Big endian\n"); break; case 0x4241: printf("ASCII Little endian\n"); break; case 0xC1C2: printf("EBCDIC Big endian\n"); break; case 0xC2C1: printf("EBCDIC Little endian\n"); break; }
编辑:
发现甚至在预处理器中的一种方式:
#if 'AB' == 0x4142 #error "ASCII Big endian\n" #elif 'AB' == 0x4241 #error "ASCII Little endian\n" #elif 'AB' == 0xC1C2 #error "EBCDIC Big endian\n" #elif 'AB' == 0xC2C1 #error "EBCDIC Little endian\n" #else #error "unknown coding and endianness\n" #endif
有人问之前,多字节字符常量是ANSI-C(甚至是C90),但是已经定义了实现。 这是我为他们find的第一个有用的应用程序。