在编译时确定字节序

时间:2010-11-21 19:55:25

标签: c endianness

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

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

13 个答案:

答案 0 :(得分:33)

这是用于编译时检查

您可以使用来自升级头文件endian.hpp的信息,该信息涵盖许多平台。

编辑运行时检查

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

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

编辑思考

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

答案 1 :(得分:31)

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

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

#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

如果你无法从文档中找到你的编译器使用的预定义宏,你也可以尝试强制它来吐出预定义宏的完整列表并从那里猜测什么会起作用(用ENDIAN,ORDER查找任何东西,或其中的处理器体系结构名称)。 This page列出了在不同编译器中执行此操作的许多方法:

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)

最后,为了完善它,Microsoft Visual C / C ++编译器是奇怪的,并没有上述任何一个。幸运的是,他们已经记录了他们的预定义宏here,您可以使用目标处理器架构来推断字节序。虽然Windows中所有当前支持的处理器都是little-endian(_M_IX86_M_X64_M_IA64_M_ARM是little-endian,但是一些历史上支持的处理器,如PowerPC(_M_PPC)是大端的。但更相关的是,Xbox 360是一款大端的PowerPC机器,所以如果你正在编写一个跨平台的库头,那么检查_M_PPC就不会有什么坏处。

答案 2 :(得分:16)

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

#define I_AM_LITTLE (((union { unsigned x; unsigned char c; }){1}).c)

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

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

答案 3 :(得分:10)

有趣的阅读C FAQ

  

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

  #if 'ABCD' == 0x41424344
     

但这也不可靠。

答案 4 :(得分:7)

我想扩展为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 conform ,其中type punning被禁止。一次只能有一个工会成员活动。使用符合标准的编译器,您将收到错误。

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

更新2

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

答案 5 :(得分: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);
}

答案 6 :(得分:3)

来自Finally, one-line endianness detection in the C preprocessor

#include <stdint.h>

#define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100)

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

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

答案 7 :(得分:1)

我曾经使用过像这样的结构:

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_BYTELO_BYTE变量被完全替换,甚至pByte acces在汇编程序中被等同于*(unit16_t *pByte) = 0x1020;替换。

这是编译时间。

答案 8 :(得分:0)

据我所知,不是在编译期间。

在运行时,您可以执行简单的检查,例如将多字节值设置为已知的位字符串,并检查导致的字节数。例如,使用union,

typedef union {
    uint32_t word;
    uint8_t bytes[4];
} byte_check;

或者施放,

uint32_t word;
uint8_t * bytes = &word;

请注意,对于完全便携式字节序检查,您需要考虑big-endian,little-endian和mixed-endian系统。

答案 9 :(得分:0)

EDIT2:此方法不起作用。多字节常量的表示是特定于编译器/平台的,不能可靠地使用。 链接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),但是是实现定义的。这是我为他们找到的第一个有用的应用程序。

答案 10 :(得分:0)

使用CMake TestBigEndian作为

INCLUDE(TestBigEndian)
TEST_BIG_ENDIAN(ENDIAN)
IF (ENDIAN)
    # big endian
ELSE (ENDIAN)
    # little endian
ENDIF (ENDIAN)

答案 11 :(得分:0)

就我而言,我决定使用一种中间方法:尝试使用宏,如果它们不存在,或者我们找不到它们,则在运行时执行。这是一种适用于GNU编译器的文件:

#define II      0x4949     // arbitrary values != 1; examples are
#define MM      0x4D4D     // taken from the TIFF standard

int
#if defined __BYTE_ORDER__ && __BYTE_ORDER__ == __LITTLE_ENDIAN
     const host_endian = II;
# elif defined __BYTE_ORDER__ && __BYTE_ORDER__ == __BIG__ENDIAN
     const host_endian = MM;
#else
#define _no_BYTE_ORDER
     host_endian = 1;            // plain "int", not "int const" !
#endif

,然后在实际代码中:

int main(int argc, char **argv) {
#ifdef _no_BYTE_ORDER
    host_endian = * (char *) &host_endian ? II : MM;
#undef _no_BYTE_ORDER
#endif

// .... your code here, for instance:
printf("Endedness: %s\n", host_endian == II ? "little-endian"
                                            : "big-endian");

return 0;
}

另一方面,正如原始海报所指出的那样,运行时检查的开销非常小(两行代码和微秒的时间),因此几乎不值得在预处理器中进行尝试

答案 12 :(得分:0)

我从Rapidjson库中获取了

#define BYTEORDER_LITTLE_ENDIAN 0 // Little endian machine.
#define BYTEORDER_BIG_ENDIAN 1 // Big endian machine.

//#define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN

#ifndef BYTEORDER_ENDIAN
    // Detect with GCC 4.6's macro.
#   if defined(__BYTE_ORDER__)
#       if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
#           define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#       elif (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#           define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
#       else
#           error "Unknown machine byteorder endianness detected. User needs to define BYTEORDER_ENDIAN."
#       endif
    // Detect with GLIBC's endian.h.
#   elif defined(__GLIBC__)
#       include <endian.h>
#       if (__BYTE_ORDER == __LITTLE_ENDIAN)
#           define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#       elif (__BYTE_ORDER == __BIG_ENDIAN)
#           define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
#       else
#           error "Unknown machine byteorder endianness detected. User needs to define BYTEORDER_ENDIAN."
#       endif
    // Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro.
#   elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
#       define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#   elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
#       define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
    // Detect with architecture macros.
#   elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__)
#       define BYTEORDER_ENDIAN BYTEORDER_BIG_ENDIAN
#   elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__)
#       define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#   elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
#       define BYTEORDER_ENDIAN BYTEORDER_LITTLE_ENDIAN
#   else
#       error "Unknown machine byteorder endianness detected. User needs to define BYTEORDER_ENDIAN."
#   endif
#endif