是否有一种编程方法来检测您使用的是大端序还是小端序体系结构?我需要能够编写将在英特尔或PPC系统上执行的代码,并使用完全相同的代码(即,没有条件编译)。


当前回答

如果你可以使用c++ 20编译器,比如GCC 8+或Clang 7+,你可以使用std::endian。

注意:std::endian从<type_traits>开始,但在2019年科隆会议上被移动到<bit>。GCC 8、Clang 7、8、9在<type_traits>, GCC 9+和Clang 10+在<bit>。

#include <bit>

if constexpr (std::endian::native == std::endian::big)
{
    // Big-endian system
}
else if constexpr (std::endian::native == std::endian::little)
{
    // Little-endian system
}
else
{
    // Something else
}

其他回答

不要使用联合号!

c++不允许通过联合的类型双关语! 从不是最后写入的联合字段读取是未定义的行为! 许多编译器支持这样做作为扩展,但语言不能保证。

更多细节请参见以下答案:

https://stackoverflow.com/a/11996970


只有两个有效的答案可以保证是可移植的。

第一个答案,如果你有一个支持c++ 20的系统, 是从<bit>标头使用std::endian。

C++20 起

constexpr bool is_little_endian = (std::endian::native == std::endian::little);

在c++ 20之前,唯一有效的答案是存储一个整数,然后通过类型双关检查它的第一个字节。 与联合的使用不同,这是c++类型系统明确允许的。

同样重要的是要记住,为了获得最佳的可移植性,应该使用static_cast, 因为reinterpret_cast是实现定义的。

如果程序试图通过非下列类型之一的glvalue访问对象的存储值,则行为未定义: ... char或unsigned char类型。

c++ 11 Onwards

enum class endianness
{
    little = 0,
    big = 1,
};

inline endianness get_system_endianness()
{
    const int value { 0x01 };
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01) ? endianness::little : endianness::big;
}

c++ 11开始(没有enum)

inline bool is_system_little_endian()
{
    const int value { 0x01 };
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01);
}

c++ 98 / c++ 03

inline bool is_system_little_endian()
{
    const int value = 0x01;
    const void * address = static_cast<const void *>(&value);
    const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
    return (*least_significant_address == 0x01);
}

声明一个int变量:

int variable = 0xFF;

现在使用char*指针指向它的各个部分,并检查这些部分中有什么。

char* startPart = reinterpret_cast<char*>( &variable );
char* endPart = reinterpret_cast<char*>( &variable ) + sizeof( int ) - 1;

根据哪一个指向0xFF字节,现在您可以检测到字节顺序。这需要sizeof(int) > sizeof(char),但对于所讨论的平台绝对是正确的。

你可以通过设置int和屏蔽位来做到这一点,但可能最简单的方法是使用内置的网络字节转换操作(因为网络字节顺序总是大端序)。

if ( htonl(47) == 47 ) {
  // Big endian
} else {
  // Little endian.
}

一点点摆弄可能会更快,但这种方法简单,直接,几乎不可能搞砸。

我会这样做:

bool isBigEndian() {
    static unsigned long x(1);
    static bool result(reinterpret_cast<unsigned char*>(&x)[0] == 0);
    return result;
}

沿着这些思路,您将得到一个只进行一次计算的省时函数。

我不喜欢基于类型双关的方法——它经常会被编译器警告。这正是工会存在的意义!

bool is_big_endian(void)
{
    union {
        uint32_t i;
        char c[4];
    } bint = {0x01020304};

    return bint.c[0] == 1;
}

这个原则等同于其他人建议的类型大小写,但这更清楚——并且根据C99,它保证是正确的。与直接指针强制转换相比,GCC更喜欢这种方法。

这也比在编译时修复字节序要好得多——对于支持多架构的操作系统(例如Mac OS X上的胖二进制),这对ppc/i386都适用,否则很容易把事情搞砸。