C宏定义确定大端或小端机?

时间:2010-01-20 09:43:41

标签: c architecture macros endianness

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

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

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

19 个答案:

答案 0 :(得分:92)

支持任意字节顺序的代码,准备放入名为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

答案 1 :(得分:44)

如果您的编译器支持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];
}

答案 2 :(得分:35)

没有标准,但在包括<endian.h>在内的许多系统上都会为您提供一些要查找的定义。

答案 3 :(得分:25)

要在运行时检测字节顺序,您必须能够引用内存。如果坚持使用标准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宏。

答案 4 :(得分:19)

如果您只想依赖预处理器,则必须找出预定义符号列表。预处理器算术没有寻址的概念。

Mac上的GCC 定义__LITTLE_ENDIAN____BIG_ENDIAN__

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

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

答案 5 :(得分:15)

我相信这就是要求的。 我只在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();之类的代码。最坏的情况:编译器没有进行优化,你仍然得到正确的代码,但速度稍慢。

答案 6 :(得分:9)

如果您正在寻找编译时测试并且正在使用gcc,则可以执行以下操作:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__

有关详细信息,请参阅gcc documentation

答案 7 :(得分:8)

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

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

哪个GCC将在编译时评估。

答案 8 :(得分:7)

'C网络库'提供处理字节序的功能。即htons(),htonl(),ntohs()和ntohl()...其中n是“network”(即big-endian),h是“host”(即运行the的机器的endian')码)。

这些明显的“函数”(通常)定义为宏[请参阅&lt; netinet / in.h&gt;],因此使用它们没有运行时开销。

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

#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);

答案 9 :(得分:6)

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

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

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

答案 10 :(得分:5)

虽然没有可移植的#define或依赖的东西,但平台确实提供了用于转换到'host'端的标准功能。

通常,您使用“network endian”( BIG endian)进行存储 - 磁盘或网络存储,使用主机端进行本地计算(在x86上进行本地计算 LITTLE endian)。您可以使用htons()ntohs()以及朋友在两者之间进行转换。

答案 11 :(得分:3)

试试这个:

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

   TEST;
}

答案 12 :(得分:3)

#include <stdint.h>
#define IS_LITTLE_ENDIAN (*(uint16_t*)"\0\1">>8)
#define IS_BIG_ENDIAN (*(uint16_t*)"\1\0">>8)

答案 13 :(得分:3)

不要忘记字节序不是整个故事 - char的大小可能不是8位(例如DSP),不能保证2的补码否定(例如Cray),可能需要严格对齐(例如SPARC,当未对齐时,ARM也会弹入中端等等。

最好定位一个特定的 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很好地汇编了这些定义。)

答案 14 :(得分:1)

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

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

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

https://godbolt.org/z/GJGNE2this的答案,

https://godbolt.org/z/Yv-pyJ来回答this,依此类推。

要解决此问题,您将需要使用volatile限定词。 Yogeesh H T的答案是当今现实生活中最接近的答案,但是由于Christoph提出了更全面的解决方案,因此对其answer稍作修正即可使答案完整,只需添加{{ 1}}到联合声明:volatile

这将确保确定端序所需的存储和读取。

答案 15 :(得分:1)

如果您转储预处理程序#defines

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

通常,您会找到可以帮助您的东西。具有编译时逻辑。

#define __LITTLE_ENDIAN__ 1
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__

各种编译器可能有不同的定义。

答案 16 :(得分:0)

我的回答并不像要求的那样但如果您的系统是小端或大端,那么找到非常简单?

<强>代码:

#include<stdio.h>

int main()
{
  int a = 1;
  char *b;

  b = (char *)&a;
  if (*b)
    printf("Little Endian\n");
  else
    printf("Big Endian\n");
}

答案 17 :(得分:0)

用于检查系统是小端还是大印度的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");

答案 18 :(得分:-3)

查找endiannes的宏

#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;
}