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

man*_*m-n 101 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);
}
Run Code Online (Sandbox Code Playgroud)

Chr*_*oph 94

支持任意字节顺序的代码,准备放入一个名为的文件中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
Run Code Online (Sandbox Code Playgroud)

您将检查通过的小端系统

O32_HOST_ORDER == O32_LITTLE_ENDIAN
Run Code Online (Sandbox Code Playgroud)

  • 这不会让你在运行之前决定*endian-ness.以下无法编译,因为./**isLittleEndian :: result - > 0或1*/struct isLittleEndian {enum isLittleEndianResult {result =(O32_HOST_ORDER == O32_LITTLE_ENDIAN)}; }; (10认同)
  • 为什么`char`?如果这种类型不可用(可以通过`#if UINT8_MAX`检查),最好使用`uint8_t`并失败.请注意,`CHAR_BIT`独立于`uint8_t`. (8认同)
  • 在运行时获得结果是不可能的吗? (3认同)
  • 为了完整性,让我再混合一下:`O32_HONEYWELL_ENDIAN = 0x02030001ul/*霍尼韦尔316*/` (3认同)
  • 这是C ++中的UB:/sf/ask/796124241/?lq=1 (2认同)
  • 请参阅https://port70.net/~nsz/c/c11/n1570.html#6.2.6.1p7。该宏不可移植,并且依赖于 C 语言规范中未指定的行为。尽管这可能适用于许多系统,但它在技术上并不可移植。 (2认同)

caf*_*caf 44

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

#define IS_BIG_ENDIAN (!*(unsigned char *)&(uint16_t){1})
Run Code Online (Sandbox Code Playgroud)

要么:

#define IS_BIG_ENDIAN (!(union { uint16_t u16; unsigned char c; }){ .u16 = 1 }.c)
Run Code Online (Sandbox Code Playgroud)

通常,您应该尝试编写不依赖于主机平台的字节顺序的代码.


与host-endianness无关的实现示例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];
}
Run Code Online (Sandbox Code Playgroud)

  • 我还应该添加记录,"(*(uint16_t*)"\ 0\xff"<0x100)"不会编译成常量,无论我如何优化,至少使用gcc 4.5.2.它总是创建可执行代码. (6认同)
  • "你应该尝试编写不依赖于主机平台的字节顺序的代码".不幸的是我的请求,"我知道我们正在写一个POSIX兼容层,但我不想实现ntoh,因为它取决于主机平台的字节顺序"总是被置若罔闻;-).图形格式处理和转换代码是我见过的另一个主要候选者 - 你不希望所有时间都关闭调用ntohl. (3认同)
  • 您可以以不依赖于主机平台的字节顺序的方式实现`ntohl`. (3认同)
  • @AliVeli:我在答案中添加了一个示例实现. (2认同)

Ign*_*ams 39

没有标准,但在许多系统上,包括<endian.h>将给你一些定义来寻找.

  • 使用`#if __BYTE_ORDER == __LITTLE_ENDIAN`和`#elif __BYTE_ORDER == __BIG_ENDIAN`测试字节顺序.然后生成一个"#error". (28认同)
  • `<endian.h>`在Windows上不可用 (5认同)
  • @To1ne 我怀疑 Endianness 与 Windows 相关,因为 Windows(至少目前)仅在 x86 和 ARM 计算机上运行。x86 始终是 LE,而 ARM 可配置为使用任一架构。 (3认同)
  • [Android](https://android.googlesource.com/platform/system/core/+/master/include/utils/Endian.h) 和 [Chromium](https://chromium.googlesource.com/aosp/platform /system/core/+/master/include/utils/Endian.h) 项目使用 [`endian.h`](https://linux.die.net/include/endian.h) 除非 [`__APPLE__`]( https://gcc.gnu.org/onlinedocs/cpp/System-specific-Predefined-Macros.html#System-specific-Predefined-Macros) 或 [`_WIN32`](https://gcc.gnu.org/onlinedocs /cpp/System-specific-Predefined-Macros.html#System-specific-Predefined-Macros) 被定义。 (2认同)

Nor*_*sey 27

要在运行时检测字节序,您必须能够引用内存.如果坚持使用标准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))
Run Code Online (Sandbox Code Playgroud)

然后你可以随意使用ENDIANNESS宏.

  • 我喜欢这个,因为它承认除了小而大之外的字节序的存在. (6认同)
  • 说到这一点,可能值得调用宏INT_ENDIANNESS,甚至是UINT32_T_ENDIANNESS,因为它只测试一种类型的存储表示.有一个ARM ABI,其中整数类型是little-endian,但是double是中端的(每个单词都是little-endian,但是带有符号位的单词在另一个单词之前).我可以告诉你,这让编译团队在一天左右的时间内引起了一些兴奋. (6认同)

Gre*_*osz 19

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

Mac上的 GCC 定义__LITTLE_ENDIAN____BIG_ENDIAN__

$ gcc -E -dM - < /dev/null |grep ENDIAN
#define __LITTLE_ENDIAN__ 1
Run Code Online (Sandbox Code Playgroud)

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

  • 虽然GCC 4.0.1和4.2.1在Macintosh上定义它们,但Linux上的GCC 4.1.2似乎没有定义这些宏.因此,即使您被允许指定使用哪个编译器,它也不是跨平台开发的可靠方法. (6认同)

小智 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
Run Code Online (Sandbox Code Playgroud)

作为附注(特定于编译器),使用积极的编译器,您可以使用"死代码消除"优化来实现与编译时相同的效果,#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; // ?
    }
Run Code Online (Sandbox Code Playgroud)

以上依赖于编译器可以识别在编译时的常数值的事实,完全去除内的代码if (false) { ... },并替换如代码if (true) { foo(); }foo();最坏的情况:编译器不会做了优化,你仍然可以得到正确的代码,但有点慢.

  • 由于多字符字符常量,gcc也会抛出错误.因此,不便携. (3认同)
  • 什么编译器让你写''ABCD'`? (2认同)
  • 许多编译器将在宽松的合规模式中允许多字节字符常量,但是使用`clang -Wpedantic -Werror -Wall -ansi foo.c`运行顶部部分并且它将出错.(Clang和这个具体:`-Wfour-char-constants -Werror`) (2认同)

Jér*_*ler 9

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

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅gcc文档.

  • 对于任何使用gcc的人来说,这绝对是最好的答案 (3认同)
  • `__BYTE_ORDER__`自GCC 4.6起可用 (2认同)

u0b*_*6ae 8

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

#define IS_LITTLE_ENDIAN (1 == *(unsigned char *)&(const int){1})
Run Code Online (Sandbox Code Playgroud)

哪个GCC将在编译时进行评估.

  • 哦,如果不是 GCC 怎么办? (2认同)

Sam*_*m P 8

如果你转储预处理器#defines

gcc -dM -E - < /dev/null
g++ -dM -E -x c++ - < /dev/null
Run Code Online (Sandbox Code Playgroud)

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

#define __LITTLE_ENDIAN__ 1
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
Run Code Online (Sandbox Code Playgroud)

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


Blu*_*hip 7

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

这些明显的"函数"(通常)定义为宏[请参阅<netinet/in.h>],因此使用它们没有运行时开销.

以下宏使用这些"函数"来评估字节序.

#include <arpa/inet.h>
#define  IS_BIG_ENDIAN     (1 == htons(1))
#define  IS_LITTLE_ENDIAN  (!IS_BIG_ENDIAN)
Run Code Online (Sandbox Code Playgroud)

此外:

我唯一需要知道系统的字节序的时候就是我写出一个变量[到一个文件/其他],这个变量可能被另一个未知字节序系统读入(用于跨平台兼容性) )...在这些情况下,您可能更喜欢直接使用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);
Run Code Online (Sandbox Code Playgroud)


小智 6

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

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

static int s_endianess = 0;
#define ENDIANESS() ((s_endianess = 1), (*(unsigned char*) &s_endianess) == 0)
Run Code Online (Sandbox Code Playgroud)


Wil*_*ill 5

虽然没有可移植的#define或依赖的东西,但平台确实提供了与"主机"端口进行转换的标准功能.

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


rus*_*tyx 5

不要忘记字节序并不是全部 - 大小char可能不是 8 位(例如 DSP),不保证二进制补码否定(例如 Cray),可能需要严格对齐(例如 SPARC,ARM 也进入中间) -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
}
Run Code Online (Sandbox Code Playgroud)

请注意,不幸的是,该解决方案也不是超级可移植的,因为它取决于特定于编译器的定义(没有标准,但这里是此类定义的一个很好的编译)。