我有一个性能关键的C代码需要在各种平台上工作.其中一些是小端,另一些是大端.
基于宏观检测,检测结束性目前是不完美的过程.但是很难确定宏检测是否适用于系统和编译器的所有组合.欢迎来到便携式代码世界.
检测endianess的一种相对安全的方法是使用运行时测试,并希望它将被编译器优化.这些方面的东西:
static const int one = 1;
#define IS_LITTLE_ENDIAN (*(char*)(&one))
Run Code Online (Sandbox Code Playgroud)
一般来说它有效.编译器应正确检测到该宏的结果对于给定的体系结构始终是相同的(1表示小端,0表示大端),只需完全删除内存访问和关联的分支.
我的问题是:总是这样吗?我们是否可以期望编译器始终正确理解此测试,并始终正确地优化它?(假设-O2/-O3或等效的优化级别,当然不适用于调试代码)
我特别担心双端CPU,比如ARM.由于这样的CPU可以是大端或小端,取决于OS参数,编译器可能难以"硬连线"这样的端序测试.另一方面,我不希望应用程序以"任何两种选择模式"工作:我想它应该编译为一个精确和明确的endianess.因此,IS_LITTLE_ENDIAN应始终保持相同.
无论如何,我要求遇到遇到这种情况的人的经历.由于我目前没有双端CPU和编译器,我无法测试和观察上述假设.
[ 编辑 ] @Brandin建议"保存宏的结果",使其成为一个变量.我猜他提出这样的事情:
static const int one = 1;
static const int isLittleEndian = *(char*)(&one);
Run Code Online (Sandbox Code Playgroud)
由于在编译时会计算静态const int,因此确实可以保证编译器必须知道isLittleEndian的值,因此可以正确地优化使用此变量的分支.
不幸的是,它不起作用.上述声明导致以下编译错误:
error: initializer element is not constant
Run Code Online (Sandbox Code Playgroud)
我想那是因为在编译时无法评估一个(指针地址).
@ HuStmpHrrr的变体,使用union,看起来更好:没有要评估的指针地址.不幸的是,它不能更好地工作,并导致相同的编译错误.
我想这是因为编译器认为联合不够简单,不能用作静态const初始化的值.
所以我们回到了开头,用宏来代替.
同样的想法,但不同的技巧.这段代码也可以.
union {
int num;
char[sizeof(int)] bytes;
} endian;
endian.num = 1;
Run Code Online (Sandbox Code Playgroud)
然后endian.bytes[0]用来判断.
这样,事情变得更加自然,编译器应该做一些事情,因为这很容易被简单的数据流优化器实现跟踪.
endian.bytes[0] 应该缩小到一个常数.
无论如何,这种方式依赖于编译器.