我有一些模板化的低级序列化代码,我需要在编译时明确知道系统的字节顺序(因为模板专门根据系统的字节顺序)。

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

希望这能使我的情况变得清晰。

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

有帮助吗?

解决方案

如果你正在使用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;
}

其他提示

在编译时没有可移植的方法,最好的办法是使用 Boost endian宏或模仿他们使用的方法。

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

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

此答案基于以下规范(这是为了清晰起见):

  

语言: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&视窗

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

const uint8_t IsLittleEndian = char (0x0001);

#define IsLittleEndian char (0x0001)

这两个神奇的声明宝石充分利用了处理器如何在内存中存储16位值。

在“Little Endian”上处理器,如Intel和AMD芯片组,16位值存储在 [低位/最低有效字节] [高位/最高有效字节] 方式中(括号表示内存中的一个字节) )。

在“Big Endian”上处理器,如PowerPC,Sun Sparc和IBM S / 390芯片组,16位值存储在 [高阶/最高有效字节] [低阶/最低有效字节] 方式中。

例如,当我们将一个16位(两个字节)的值存储到C ++ uint16_t (在C ++ v11中定义的类型,以及之后的类型)时,假设 0x1234 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>

当我们使用代码片段 char(0x0001)将16位值 0x0001 转换为char(8位)时,编译器使用第一个内存偏移量新值的16位值。这是另一张图表,显示了在“Little Endian”和“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”上测试上面的演示。处理器,所以我将代码基于我在网络上找到的信息。感谢M.M向我指出了显而易见的事实。

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

#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”上亲

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top