32

我有一些模板化的低级序列化代码,我显然需要在编译时知道系统的字节序(因为模板专门基于系统的字节序)。

现在我有一个带有一些平台定义的标题,但我宁愿通过一些模板化测试(如 static_assert 或 boost_if)做出关于字节顺序的断言。原因是我的代码需要在各种机器、许多专业供应商的机器上编译和运行,并且可能在 2008 年不存在的设备上运行,所以我真的无法猜测到那个标题年份可能需要什么在路上。由于代码库的预期寿命约为 10 年。所以我不能永远遵循代码。

希望这能说明我的情况。

那么有没有人知道可以确定字节顺序的编译时测试,而不依赖于供应商特定的定义?

4

4 回答 4

21

如果您使用的是 autoconf,则可以使用AC_C_BIGENDIAN宏,它可以保证正常工作(WORDS_BIGENDIAN默认设置定义)

或者,您可以尝试以下类似的方法(取自 autoconf)以获得可能会被优化的测试(至少,GCC 删除了另一个分支)

int is_big_endian()
{
    union {
        long int l;
        char c[sizeof (long int)];
    } u;

    u.l = 1;

    if (u.c[sizeof(long int)-1] == 1)
    {
        return 1;
    }
    else
        return 0;
}
于 2008-11-11T10:28:54.023 回答
18

在编译时没有可移植的方法来执行此操作,最好的选择可能是使用Boost endian 宏或模拟它们使用的方法。

于 2008-11-11T06:38:55.843 回答
5

嗯,这是一个有趣的问题。我敢打赌,这是不可能的。我认为您必须继续使用宏,并使用BOOST_STATIC_ASSERT(!BIG_ENDIAN);, 或static_assertc++0x。我认为这是因为如果您的执行环境,endian'nes 是一个属性。但是,在编译时会考虑 static_assert。

我建议您查看新的GNU gold ELF 链接器的代码。它的作者 Ian Lance Taylor 使用模板在编译时选择正确的字节序,以确保在运行时获得最佳性能。他明确地实例化了所有可能的字节序,因此他仍然有模板定义和声明的单独编译(不是头文件中的所有模板)。他的代码很棒。

于 2008-11-11T06:33:23.640 回答
-1

该答案基于以下规格(为了清楚起见):

语言:C++ v17,64 位
编译器:g++ v8(GNU 编译器集合https://www.gnu.org/software/gcc/)和 MingW 8.1.0 工具链(https://sourceforge.net/projects/mingw -w64/files/ )
操作系统:Linux Mint & Windows

以下两行代码可用于成功检测处理器的字节顺序:

const uint8_t IsLittleEndian = char (0x0001);

或者

#define IsLittleEndian char (0x0001)

这两个神奇的语句宝石利用了处理器在内存中存储 16 位值的方式。

在“Little Endian”处理器上,如 Intel 和 AMD 芯片组,16 位值以某种[low order/least significant byte][high order/most significant byte]方式存储(括号表示内存中的一个字节)。

在“Big Endian”处理器上,如 PowerPC、Sun Sparc 和 IBM S/390 芯片组,16 位值以一种[high order/most significant byte][low order/least significant byte]方式存储。

例如,当我们将 16 位(两个字节)值存储0x1234到 C++ uint16_t(在 C++ v11 中定义的类型,以及后来的https://en.cppreference.com/w/cpp/types/integer)中时“Little Endian”处理器上的变量,然后查看存储该值的内存块,您将找到字节序列,[34][12].

在“Big Endian 处理器”上,该0x1234值存储为[12][34].

这是一个小演示,可帮助演示各种大小的 C++ 整数变量如何存储在大小端处理器的内存中:

#define __STDC_FORMAT_MACROS // Required for the MingW toolchain
#include <iostream>
#include <inttypes.h>

const uint8_t IsLittleEndian = char (0x0001);
//#define IsLittleEndian char (0x0001)

std::string CurrentEndianMsg;
std::string OppositeEndianMsg;

template <typename IntegerType>
void PrintIntegerDetails(IntegerType IntegerValue)
{
    uint16_t SizeOfIntegerValue = sizeof(IntegerValue);
    int8_t i;

    std::cout << "Integer size (in bytes): " << SizeOfIntegerValue << "\n";
    std::cout << "Integer value (Decimal): " << IntegerValue << "\n";
    std::cout << "Integer value (Hexidecimal): ";

    switch (SizeOfIntegerValue)
    {
        case 2: printf("0x%04X\n", (unsigned int) IntegerValue);
                break;
        case 4: printf("0x%08X\n", (unsigned int) IntegerValue);
                break;
        case 8: printf("0x%016" PRIX64 "\n", (uint64_t) IntegerValue);
                break;
    }

    std::cout << "Integer stored in memory in byte order:\n";
    std::cout << "        " << CurrentEndianMsg << " processor [current]: ";

    for(i = 0; i < SizeOfIntegerValue; i++)https://stackoverflow.com/qhttps://stackoverflow.com/questions/280162/is-there-a-way-to-do-a-c-style-compile-time-assertion-to-determine-machines-e/54175491#54175491uestions/280162/is-there-a-way-to-do-a-c-style-compile-time-assertion-to-determine-machines-e/54175491#54175491
    {
        printf("%02X ", (((unsigned char*) &IntegerValue)[i]));
    }

    std::cout << "\n        " << OppositeEndianMsg << " processor  [simulated]: ";

    for(i = SizeOfIntegerValue - 1; i >= 0; i--)
    {
        printf("%02X ", (((unsigned char*) &IntegerValue)[i]));
    }

    std::cout << "\n\n";
}


int main()
{
    uint16_t ValueUInt16a = 0x0001;
    uint16_t ValueUInt16b = 0x1234;
    uint32_t ValueUInt32a = 0x00000001;
    uint32_t ValueUInt32b = 0x12345678;
    uint64_t ValueUInt64a = 0x0000000000000001;
    uint64_t ValueUInt64b = 0x123456789ABCDEF0;

    std::cout << "Current processor endianness: ";

    switch (IsLittleEndian) {
        case 0: CurrentEndianMsg = "Big Endian";
                OppositeEndianMsg = "Little Endian";
                break;
        case 1: CurrentEndianMsg = "Little Endian";
                OppositeEndianMsg = "Big Endian";
                break;
    }

    std::cout << CurrentEndianMsg << "\n\n";

    PrintIntegerDetails(ValueUInt16a);
    PrintIntegerDetails(ValueUInt16b);
    PrintIntegerDetails(ValueUInt32a);
    PrintIntegerDetails(ValueUInt32b);
    PrintIntegerDetails(ValueUInt64a);
    PrintIntegerDetails(ValueUInt64b);

    return 0;
}

这是我机器上演示的输出:

Current processor endianness: Little Endian

Integer size (in bytes): 2
Integer value (Decinal): 1
Integer value (Hexidecimal): 0x0001
Integer stored in memory in byte order:
        Little Endian processor [current]: 01 00
        Big Endian processor  [simulated]: 00 01

Integer size (in bytes): 2
Integer value (Decinal): 4660
Integer value (Hexidecimal): 0x1234
Integer stored in memory in byte order:
        Little Endian processor [current]: 34 12
        Big Endian processor  [simulated]: 12 34

Integer size (in bytes): 4
Integer value (Decinal): 1
Integer value (Hexidecimal): 0x00000001
Integer stored in memory in byte order:
        Little Endian processor [current]: 01 00 00 00
        Big Endian processor  [simulated]: 00 00 00 01

Integer size (in bytes): 4
Integer value (Decinal): 305419896
Integer value (Hexidecimal): 0x12345678
Integer stored in memory in byte order:
        Little Endian processor [current]: 78 56 34 12
        Big Endian processor  [simulated]: 12 34 56 78

Integer size (in bytes): 8
Integer value (Decinal): 1
Integer value (Hexidecimal): 0x0000000000000001
Integer stored in memory in byte order:
        Little Endian processor [current]: 01 00 00 00 00 00 00 00
        Big Endian processor  [simulated]: 00 00 00 00 00 00 00 01

Integer size (in bytes): 8
Integer value (Decinal): 13117684467463790320
Integer value (Hexidecimal): 0x123456789ABCDEF0While the process
Integer stored in memory in byte order:
        Little Endian processor [current]: F0 DE BC 9A 78 56 34 12
        Big Endian processor  [simulated]: 12 34 56 78 9A BC DE F0

我在 Linux Mint 中使用 GNU C++ 工具链编写了这个演示,并且没有办法在其他风格的 C++ 中进行测试,例如 Visual Studio 或 MingW 工具链,所以我不知道在它们中编译需要什么,也不知道我现在可以访问 Windows。

但是,我的一个朋友用 MingW,64 位 (x86_64-8.1.0-release-win32-seh-rt_v6-rev0) 测试了代码,它有错误。经过一番研究,我发现我需要#define __STDC_FORMAT_MACROS在代码顶部添加一行,以便使用 MingW 进行编译。

现在我们可以直观地看到 16 位值是如何存储在内存中的,让我们看看如何利用它来确定处理器的字节序。

为了在可视化 16 位值在内存中的存储方式方面提供一些额外帮助,让我们看一下下面的图表:

16-Bit Value (Hex):  0x1234

Memory Offset:       [00] [01]
                     ---------
Memory Byte Values:  [34] [12]  <Little Endian>
                     [12] [34]  <Big Endian>

================================================

16-Bit Value (Hex):  0x0001

Memory Offset:       [00] [01]
                     ---------
Memory Byte Values:  [01] [00]  <Little Endian>
                     [00] [01]  <Big Endian>

当我们使用片段将 16 位值0x0001转换为 char(8 位)时char (0x0001),编译器使用 16 位值的第一个内存偏移量作为新值。这是另一个图表,显示了“Little Endian”和“Big Endian”处理器上发生的情况:

Original 16-Bit Value: 0x0001

Stored in memory as: [01][00]  <-- Little Endian
                     [00][01]  <-- Big Endian

Truncate to char:    [01][xx]  <-- Little Endian
                     [01]      Final Result
                     [00][xx]  <-- Big Endian
                     [00]      Final Result

如您所见,我们可以轻松确定处理器的字节顺序。


更新:

我无法在“Big Endian”处理器上测试上面的演示,所以我将代码基于我在网上找到的信息。感谢 MM 指出对我来说显而易见的事情。

我已经更新了演示代码(如下所示)以正确测试字节顺序或处理器。

#define __STDC_FORMAT_MACROS // Required for the MingW toolchain
#include <iostream>
#include <inttypes.h>

std::string CurrentEndianMsg;
std::string OppositeEndianMsg;

template <typename IntegerType>
void PrintIntegerDetails(IntegerType IntegerValue)
{
    uint16_t SizeOfIntegerValue = sizeof(IntegerValue);
    int8_t i;

    std::cout << "Integer size (in bytes): " << SizeOfIntegerValue << "\n";
    std::cout << "Integer value (Decimal): " << IntegerValue << "\n";
    std::cout << "Integer value (Hexidecimal): ";

    switch (SizeOfIntegerValue)
    {
        case 2: printf("0x%04X\n", (unsigned int) IntegerValue);
                break;
        case 4: printf("0x%08X\n", (unsigned int) IntegerValue);
                break;
        case 8: printf("0x%016" PRIX64 "\n", (uint64_t) IntegerValue);
                break;
    }

    std::cout << "Integer stored in memory in byte order:\n";
    std::cout << "        " << CurrentEndianMsg << " processor [current]: ";

    for(i = 0; i < SizeOfIntegerValue; i++)
    {
        printf("%02X ", (((unsigned char*) &IntegerValue)[i]));
    }

    std::cout << "\n        " << OppositeEndianMsg << " processor  [simulated]: ";

    for(i = SizeOfIntegerValue - 1; i >= 0; i--)
    {
        printf("%02X ", (((unsigned char*) &IntegerValue)[i]));
    }

    std::cout << "\n\n";
}


int main()
{
    uint16_t ValueUInt16a = 0x0001;
    uint16_t ValueUInt16b = 0x1234;
    uint32_t ValueUInt32a = 0x00000001;
    uint32_t ValueUInt32b = 0x12345678;
    uint64_t ValueUInt64a = 0x0000000000000001;
    uint64_t ValueUInt64b = 0x123456789ABCDEF0;

    uint16_t EndianTestValue = 0x0001;
    uint8_t IsLittleEndian = ((unsigned char*) &EndianTestValue)[0];

    std::cout << "Current processor endianness: ";

    switch (IsLittleEndian) {
        case 0: CurrentEndianMsg = "Big Endian";
                OppositeEndianMsg = "Little Endian";
                break;
        case 1: CurrentEndianMsg = "Little Endian";
                OppositeEndianMsg = "Big Endian";
                break;
    }

    std::cout << CurrentEndianMsg << "\n\n";

    PrintIntegerDetails(ValueUInt16a);
    PrintIntegerDetails(ValueUInt16b);
    PrintIntegerDetails(ValueUInt32a);
    PrintIntegerDetails(ValueUInt32b);
    PrintIntegerDetails(ValueUInt64a);
    PrintIntegerDetails(ValueUInt64b);

    return 0;
}

这个更新的演示创建了一个 16 位值0x0001,然后读取变量存储器中的第一个字节。如上面显示的输出所示,在“Little Endian”处理器上,该值将是 0x01。在“Big Endian”处理器上,该值为 0x00。

于 2019-01-14T03:37:25.723 回答