检测字节序

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

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

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

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

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ ... #else ... #endif 

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

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

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

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

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

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

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

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

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

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

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

在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 

(其中COMPILED_FOR_BIG_ENDIANCOMPILED_FOR_LITTLE_ENDIAN是宏#define d,根据您的预处理器字节顺序检查)

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

如前所述,检测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,但是有许多这样的预定义宏,这些宏总的来说很有可能在大多数情况下正确检测大端。

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

这是一个function就是这样:

 bool IsLittleEndian () { int i=1; return (int)*((unsigned char *)&i)==1; } 

尝试以下方法:

 if(*(char *)(int[]){1}) { /* little endian code */ } else { /* big endian code */ } 

并查看您的编译器是否在编译时解析它。 如果没有,你可能会更好运与联盟做同样的事情。 实际上我喜欢使用评估为0,1或1,0(分别)的联合来定义宏,这样我就可以做一些事情,比如访问buf[HI]buf[LO]

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

希望这可以帮助!

您无法在编译时检测到它可以在所有编译器中移植。 也许你可以在运行时更改代码来实现它 – 这是可以实现的。

使用预处理程序指令无法在C中可移植地检测字节顺序。

这来自p。 C中的 45个指针

 #include  #define BIG_ENDIAN 0 #define LITTLE_ENDIAN 1 int endian() { short int word = 0x0001; char *byte = (char *) &word; return (byte[0] ? LITTLE_ENDIAN : BIG_ENDIAN); } int main(int argc, char* argv[]) { int value; value = endian(); if (value == 1) printf("The machine is Little Endian\n"); else printf("The machine is Big Endian\n"); return 0; } 

我知道我参加这个派对已经迟到了,但这是我的看法。

 int is_big_endian() { return 1 & *(uint16_t*)"01"; } 

这是基于'0'为十进制48和'1' 49这一事实,因此'1'的LSB位设置,而'0'不设置。 我可以将它们'\x00''\x01'但我认为我的版本更具可读性。

Socket的ntohl函数可用于此目的。 资源

 // Soner #include  int main() { if (ntohl(0x12345678) == 0x12345678) { printf("big-endian\n"); } else if (ntohl(0x12345678) == 0x78563412) { printf("little-endian\n"); } else { printf("(stupid)-middle-endian\n"); } return 0; } 
 #define BIG_ENDIAN ((1 >> 1 == 0) ? 0 : 1)