将 uint8_t* 缓冲区转换为 uint16_t 并更改字节顺序

Converting uint8_t* buffer to uint16_t and changing endianness

我想处理外部图书馆提供的数据。

lib 保存数据并提供对它的访问,如下所示:

const uint8_t* data;
std::pair<const uint8_t*, const uint8_t*> getvalue() const {
  return std::make_pair(data + offset, data + length);
}

我知道当前数据包含两个 uint16_t 数字,但我需要更改它们的字节顺序。 所以总共数据是 4 个字节长并且包含这个数字:

66 4 0 0

所以我想得到两个 uint16_t 值分别为 10900 的数字。

我可以做基本的算术并在一个地方改变字节顺序:

pair<const uint8_t*, const uint8_t*> dataPtrs = library.value();
vector<uint8_t> data(dataPtrs.first, dataPtrs.second);

uint16_t first = data[1] <<8 + data[0]
uint16_t second = data[3]<<8 + data[2]

但是我想做一些更优雅的事情(如果有更好的方法来获取 uint16_ts,向量是可替换的)。

如何更好地从 uint8_t* 创建 uint16_t?如果可能的话,我会避免使用 memcpy,并使用更多 modern/safe.

Boost 有一些不错的 header-only endian library 可以工作,但它需要一个 uint16_t 输入。

为了更进一步,Boost 还提供了用于更改字节顺序的数据类型,因此我可以创建一个结构:

struct datatype {
    big_int16_buf_t     data1;
    big_int16_buf_t     data2;
}

是否可以安全地(填充、platform-dependency 等)将有效的 4 字节长 uint8_t* 转换为 datatype?也许有像这个工会这样的东西?

typedef union {
    uint8_t u8[4];
    datatype correct_data;
} mydata;

Maybe with something like this union?

没有。 C++ 中没有很好地定义联合类型双关语。

这将在假设 big_int16_buf_t 的情况下工作,因此 datatype 可以简单地复制:

datatype d{};
std::memcpy(&d, data, sizeof d);
uint16_t first = data[1] <<8 + data[0]
uint16_t second = data[3]<<8 + data[2]

However I'd like to do something more elegant

这实际上(在我看来主观上)是一种非常优雅的方式,因为它在所有系统上都以相同的方式工作。这会将数据读取为小字节序,无论 CPU 是小字节序、大字节序还是其他字节序。这很好携带。

However I'd like to do something more elegant (the vector is replaceable if there is better way for getting the uint16_ts).

矢量似乎完全没有意义。您也可以使用:

const std::uint8_t* data = dataPtrs.first;

How can I better create uint16_t from uint8_t*?

如果您确定 uint8_t 指针后面的数据确实是 uint16_t,C++ 允许:auto u16 = *static_cast<uint16_t const*>(data); 否则就是UB.

鉴于 endian value, transforming this into little endian can be done with the ntohs 功能强大(在 linux 下,其他操作系统也有类似的功能)。


但要注意,如果您持有的指针指向两个单独的 uint8_t 值,您绝不能 转换它们通过指针转换。在这种情况下,您必须手动指定哪个值去哪里(可以想象使用函数模板)。这将是最便携的解决方案,而且编译器很可能会根据 shifts 和 ors 创建高效的代码。