在写入 Buffer 类时,是否需要考虑主机的字节序?

Do I need to account for host machine's endianness when writing to my Buffer class?

提问人:Alex 提问时间:11/9/2023 最后编辑:Alex 更新时间:11/11/2023 访问量:66

问:

我正在编写一个 Buffer 类,它只是在 C++ 中保存字节。它具有各种类型的常用读写方法;16、32 和 64 位整数、浮点数和双精度数。我主要将其用于内存中加载/处理和文件 I/O,以后可能还会使用一些网络。

该类不完全依赖于主机的字节序,而是具有内置的字节序开关,因此所有读取和写入方法都将按所需顺序将给定值的基础字节读取/写入其目标。

我的问题是,我是否应该考虑运行此程序的机器的字节序,同时还要观察缓冲区的选定字节序模式?

例:

void Buffer::WriteInt32(int _int)
{
    bvec bytes;
    for (int i = 0; i < SIZE_INT32; i++)
    {
        bytes.push_back
        (
            _int >> (m_Endianness == Endian::BIG ? ((SIZE_INT32 * 8) - ((i + 1) * 8)) : i * 8)
        );
    }

    m_Bytes.insert(std::end(m_Bytes), std::begin(bytes), std::end(bytes));
    m_Length += SIZE_INT32;
}

此示例方法有效,并根据用户的偏好以大端或小端顺序写入。但是,我偷偷怀疑我应该检查运行此代码的机器的字节序,如果系统的字节序和缓冲区的设置匹配,我应该检查运行此代码的机器的字节序,并直接写入字节而不进行更改......这是我应该做的吗?由于我实际上没有选择在另一台机器上进行测试,因此我对场景可能如何发展感到困惑。

同样,对于阅读:

bool Buffer::ReadInt32(int& dest, bool useOffset, size_t offset)
{
    int val = 0;
    size_t index = useOffset ? offset : m_ReadOffset;
    if (ReadableRemaining(index) < SIZE_INT32)
        return false;

    for (auto i = 0; i < SIZE_INT32; i++)
    {
        val <<= 8;
        val |= m_Bytes[m_Endianness == Endian::BIG ? F_VAL(index, i) : R_VAL(index, i, SIZE_INT32)];
    }

    dest = val;

    m_LastReadSize = SIZE_INT32;
    if (!useOffset)
        AdvanceReadOffset();

    return true;
}

该代码将从 4 个字节检索到的值存储到给定的整数目标中;字节的观察顺序基于缓冲区的字节序设置。同样,一切正常,但是运行此代码的机器可以反转 val 的输出吗?我是否需要在此处添加代码以根据主机进行交换?

C++ 内存 缓冲区 字节序

评论

1赞 Some programmer dude 11/9/2023
当您需要在系统之间共享数据时,字节序是一个问题。即便如此,最有效的方法是仅在绝对需要时才转换数据,例如在保存/写入数据或加载/读取数据时。一旦它进入内存,它应该是主机的本机格式,在运行时不需要再进行转换。
0赞 Some programmer dude 11/9/2023
另请注意,浮点类型的字节序(如果有的话)没有明确定义。并且浮点数据可能有不同的格式,具体取决于系统。不要求本机主机格式遵循严格的IEEE754格式,可能会有变体。这使得分享这些价值观变得更加困难。这只是建议使用基于文本的格式进行共享的原因之一,字节序是另一个原因。
0赞 Alex 11/9/2023
此类将在向磁盘读取和写入二进制数据的过程中使用,这些数据很可能最终出现在相反的字节序机器上或源自相反的字节序机器。
2赞 Some programmer dude 11/9/2023
由您决定文件的格式吗?然后确定文件的单个字节序。让读取器和写入器在加载或保存数据时执行所需的任何可能的转换。在内存中时,它应该全部采用本机格式。
1赞 Pepijn Kramer 11/9/2023
警告:不仅是字节序,而且很可能是特定于平台的。所以至少不要使用 but(或sizeof(int)intstd::int32_tstd::uint32_t)

答:

0赞 Alex 11/11/2023 #1

经过一段时间的思考,我回答了自己的问题。我只是有一种困惑的阴霾,使我当时无法看清事物:)

鉴于:

  1. 大端和小端计算机将其值存储在内存中 根据其指定的布局;
  2. 我的缓冲区类只按照给定的顺序存储原始字节,以便以后在内存中和其他地方使用;
  3. 该类的用户选择写入的每个值的字节序 单独到缓冲区/从缓冲区读取,而不管机器的字节序如何;

显然,在确定每个操作的给定字节范围是否需要顺序反转之前,我需要观察机器的字节序。如果机器顺序与所需的操作顺序匹配,则不进行反转;否则,它是。

我还考虑了您的一些建议:

  • 切换到使用 std::int32_tint64_t 等以避免意外。
  • 我已经在读取和写入过程中使用我自己的硬编码 2/4/8 字节大小检查了各种类型的大小,因此切换到固定宽度类型更有意义。
  • 该项目现在检查实现是否存在 IEEE-754 浮点一致性,如果没有它,将无法编译。这是我很高兴遵守的规则。至于再现值的准确性,不同的代码将以最合适的方式处理;需要确定性的东西很可能不会依赖于基本的浮点数 -> 字节 ->不同的机器 -> 浮点数转换。
  • 特定于项目的文件格式已经设计为全面采用小端序,但也感谢您的建议。