检测字节序

Cya*_*yan 18 c endianness compile-time c-preprocessor

我正在尝试创建一个C源代码,无论目标系统的字节顺序如何,它都能正确处理I/O.

我选择了"little endian"作为我的I/O约定,这意味着,对于大端CPU,我需要在写入或读取时转换数据.

转换不是问题.我面临的问题是检测字节序,最好是在编译时(因为CPU在执行过程中不会改变字节序...).

到目前为止,我一直在使用这个:

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

它被记录为GCC预定义的宏,而Visual似乎也理解它.

但是,我收到报告说某些big_endian系统(PowerPC)的检查失败了.

所以,我正在寻找一个万无一失的解决方案,确保无论编译器和目标系统如何都能正确检测到字节顺序.好吧,他们中的大多数至少......

[编辑]:提出的大多数解决方案都依赖于"运行时测试".编译期间编译器有时可以正确评估这些测试,因此不会产生实际的运行时性能.

然而,用某种<< if (0) { ... } else { ... }>> 分支是不够的.在当前的代码实现中,变量和函数声明依赖于big_endian检测.使用if语句无法更改这些内容.

嗯,显然,有后备计划,即重写代码......

我宁愿避免这种情况,但是,它看起来像是一个越来越少的希望......

[编辑2]:我通过深度修改代码测试了"运行时测试".尽管他们正确地完成了工作,但这些测试也会影响性能.

我期待着,因为测试具有可预测的输出,编译器可以消除坏分支.但不幸的是,它并不是一直有效.MSVC是一个很好的编译器,并且成功地消除了坏分支,但是GCC的结果是混合的,这取决于版本,测试类型,以及对64位比对32位的影响更大.

真奇怪.而且这也意味着无法确保编译器处理运行时测试.

编辑3:这些天,我正在使用编译时常量联合,期望编译器将其解析为明确的是/否信号.它运作得很好:https: //godbolt.org/g/DAafKo

Cya*_*yan 16

如前所述,检测Big Endian的唯一"真正"方法是使用运行时测试.

但是,有时候,宏可能是首选.

不幸的是,我没有找到一个"测试"来检测这种情况,而是检测它们的集合.

例如,GCC建议:__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__.但是,这仅适用于最新版本,早期版本(和其他编译器)将为此测试提供错误值"true",因为NULL == NULL.所以你需要更完整的版本:defined(__BYTE_ORDER__)&&(__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)

好的,现在这适用于最新的GCC,但其他编译器呢?

您可以尝试__BIG_ENDIAN____BIG_ENDIAN_BIG_ENDIAN这往往是在大端编译器定义.

这将改善检测.但是,如果您专门针对PowerPC平台,则可以添加一些测试以改进更多检测.尝试_ARCH_PPC__PPC____PPCPPC__powerpc____powerpc或甚powerpc.将所有这些定义绑定在一起,您可以非常公平地检测大端系统,特别是powerpc,无论编译器及其版本如何.

因此,总而言之,没有"标准的预定义宏"可以保证在所有平台和编译器上检测大端CPU,但是有许多这样的预定义宏,这些宏总的来说很有可能在大多数情况下正确检测大端.

  • 为认为该答案有用的其他人写信。gcc从4.6开始支持__BYTE_ORDER__,从3.2开始支持clang (2认同)

Chr*_*utz 14

而不是寻找编译时检查,为什么不使用big-endian命令(被许多人认为是"网络命令")并使用大多数UNIX系统和Windows提供的htons/ htonl/ ntohs/ntohl函数.他们已经被定义为完成你想要做的工作.为什么重新发明轮子?


Mat*_*lia 11

在C语言的编译时,除了信任预处理器之外#define,你不能做更多的事情,并且没有标准的解决方案,因为C标准不关心字节序.

仍然,您可以添加一个在程序开始时在运行时完成的断言,以确保在编译时完成的假设为真:

inline int IsBigEndian()
{
    int i=1;
    return ! *((char *)&i);
}

/* ... */

#ifdef COMPILED_FOR_BIG_ENDIAN
assert(IsBigEndian());
#elif COMPILED_FOR_LITTLE_ENDIAN
assert(!IsBigEndian());
#else
#error "No endianness macro defined"
#endif
Run Code Online (Sandbox Code Playgroud)

(根据您的预处理器字节顺序检查,以前COMPILED_FOR_BIG_ENDIAN和之前的COMPILED_FOR_LITTLE_ENDIAN宏都是#define)


Rob*_*ahy 6

尽管有编译器定义的宏,但我认为没有一种编译时方法可以检测到这一点,因为确定架构的字节序涉及分析它在内存中存储数据的方式。

这是一个可以做到这一点的函数:

bool IsLittleEndian () {

    int i=1;

    return (int)*((unsigned char *)&i)==1;

}
Run Code Online (Sandbox Code Playgroud)


tem*_*def 6

正如其他人指出的那样,没有一种可移植的方法可以在编译时检查字节顺序。但是,一种选择是使用该autoconf工具作为构建脚本的一部分来检测系统是大端还是小端,然后使用AC_C_BIGENDIAN保存此信息的宏。从某种意义上说,这构建了一个程序,该程序在运行时检测系统是大端还是小端,然后让该程序输出信息,然后可以由主源代码静态使用。

希望这可以帮助!


R..*_*R.. 6

尝试类似:

if(*(char *)(int[]){1}) {
    /* little endian code */
} else {
    /* big endian code */
}
Run Code Online (Sandbox Code Playgroud)

并查看编译器是否在编译时解决了该问题。如果不是这样,那么您最好还是通过工会来做同样的事情。实际上,我喜欢使用分别计算为0,1或1,0的并集定义宏,以便我可以执行诸如访问buf[HI]和的操作buf[LO]

  • 它是用当前的C语言而不是C89编写的。您可能正在使用像MSVC这样的向后编译器,在这种情况下,您需要对其进行一些调整。 (3认同)