C中的字节序检测和性能

时间:2014-08-19 19:42:33

标签: c endianness

我有一个性能关键的C代码,需要在各种平台上运行。其中有些是小端,有些是大端。

基于宏观检测,检测字节顺序目前是一个不完美的过程。但很难确定宏检测是否适用于系统和编译器的所有组合。欢迎来到便携式代码世界。

检测endianess的一种相对安全的方法是使用运行时测试,并希望它将被编译器优化。这些方面的东西:

static const int one = 1;
#define IS_LITTLE_ENDIAN (*(char*)(&one))

一般来说它有效。编译器应该正确检测到这个宏的结果对于给定的体系结构总是相同的(1表示小端,0表示大端),只需删除内存访问和相关的分支。

我的问题是:情况总是这样吗?我们是否可以期望编译器始终正确理解此测试,并始终正确地优化它? (假设-O2 / -O3或等效的优化级别,当然不适用于调试代码)

我特别担心双端CPU ,例如ARM。由于这样的CPU可以是大端或小端,取决于操作系统参数,编译器可能很难“硬连线”#34;这样的endian测试。另一方面,我不希望应用程序工作在"选择端模式" :我想它应该编译为一个精确和明确的endianess。因此,IS_LITTLE_ENDIAN应始终保持相同。

无论如何,我要求遇到遇到这种情况的人的经历。由于我目前没有双端CPU和编译器,我无法测试和观察上述假设。

[修改] @Brandin建议"保存结果"宏的,使它成为一个变量。我想他建议这样的事情:

static const int one = 1;
static const int isLittleEndian = *(char*)(&one);

由于在编译时计算静态const int,它确实可以保证编译器必须知道isLittleEndian的值,因此可以正确地优化使用此变量的分支。

不幸的是,它不起作用。 上述声明导致以下编译错误:

error: initializer element is not constant

我想这是因为& one(一个指针地址)无法在编译时进行评估。

@ HuStmpHrrr的变体,使用union代替,看起来更好:没有要评估的指针地址。 不幸的是,它没有更好的工作,并导致相同的编译错误。

我想这是因为编译器认为联合不够简单,不能用作静态const初始化的值。

所以我们回到了开头,用宏来代替。

3 个答案:

答案 0 :(得分:5)

同样的想法,但不同的技巧。这段代码也可以。

union {
  int num;
  char[sizeof(int)] bytes;
} endian;

endian.num = 1;

然后使用endian.bytes[0]来判断。

这样,事情变得更加自然,编译器应该做一些事情,因为这很容易被简单的数据流优化器实现跟踪。

endian.bytes[0]应该缩小为常数。

无论如何,这种方式取决于编译器。

答案 1 :(得分:2)

忽略@OliCharlesworth在评论中链接的帖子中的小问题,我同意它的精神:你确定字节顺序真的很重要,如果确实如此,为什么?

  • 如果实际上并不重要,可移植且可能非常快速的解决方案是使用memcpy,它将按主机字节顺序复制字节。这意味着大端机器和小端机器之间的文件不可互换。

  • 如上所述,您可以根据需要将整数转换为特定的字节顺序,方法是对值进行位移操作,而不管字节顺序如何。例如,将(value >> 8) & 0xff后跟(value >> 0) & 0xff写入文件而不是原始值本身将始终以大端顺序写入16位值,并且您将始终读取高8位(最重要的)字节)首先是16位值。这基本上是像htonl这样的函数所做的,除了函数返回的值可能因为重构其底层字节表示而不同,这与简单地逐个写移位字节不同。 p>

除了“我有性能关键代码”之外没有更多上下文,任何其他指导都不可用。我已经提到的主要问题是机器的字节顺序对于这个问题如此重要的原因。

答案 2 :(得分:1)

您可以使用htonl来检测它:

#include <stdio.h>
int main()
{
        if (htonl(1) == 1)
                printf("big endian\n");
        else
                printf("little endian\n");
}