判断字节序

C++ 20 之前

根据字节序的约定,若低地址对应于数值的低位字节,则为小端方式;反之,若低地址对应于数值的高位字节,则为大端方式。下面所示的方法是最为稳妥的判断方法。

int main() {
    std::uint16_t word;
    std::uint8_t bytes[2] = {
        0x00, // 低地址字节
        0xFF  // 高地址字节
    };
    std::memcpy(&word, bytes, sizeof(word));
    if (word == 0xFF00) {
        std::cout << "small endian" << std::endl;
    } else if (word == 0x00FF) {
        std::cout << "big endian" << std::endl;
    } else {
        std::cout << "unexpected" << std::endl;
    }
}

下面的代码示例采用联合体来判断字节序,并且广泛使用,在主流编译器平台都没有问题。但实际上,使用联合体来访问同一内存位置的不同成员是未定义行为,因此应该尽量避免。

union EndianTest {
    std::uint16_t word;
    struct {
        std::uint8_t low; // 低地址字节
        std::uint8_t high; // 高地址字节
    } bytes;
};

int main() {
    EndianTest test {
        .bytes = {
            .low = 0x00,
            .high = 0xFF
        }
    };
    if (test.word == 0xFF00) {
        // 高地址字节对应数值高字节
        std::cout << "small endian" << std::endl;
    } else if (test.word = 0x00FF) {
        // 低地址字节对应数值高字节
        std::cout << "big endian" << std::endl;
    } else {
        std::cout << "unexpected" << std::endl;
    }
}

上面提及的方法是在运行时进行判断,是否可以在编译期就进行判断呢?答案是可以的,但需要使用编译期提供的预定义宏,并且需要了解不同处理器平台字节序规定,例如,x86 平台是典型的小端字节序,ARM 平台是典型的大端字节序,那么,在 MSVC 中就可以采用如下方法判断:

#if defined(_M_IX86) || defined(_M_X64)
    std::cout << "small endian" << std::endl;
#elif defined(_M_ARM) || defined(_M_ARM64)
    std::cout << "big endian" << std::endl;
// other platforms
#endif

C++ 20 及之后

C++ 20 引入了 <bit> 头文件,其中包含了 std::endian 枚举类型,可以直接在编译期判断当前系统的字节序:

#include <bit>
#include <iostream>

int main() {
    if (std::endian::native == std::endian::little) {
        std::cout << "small endian" << std::endl;
    } else if (std::endian::native == std::endian::big) {
        std::cout << "big endian" << std::endl;
    } else {
        std::cout << "unknown endian" << std::endl;
    }
}