【问题标题】:Storing integral values in a byte sequence in C++在 C++ 中以字节序列存储整数值
【发布时间】:2020-03-19 21:01:29
【问题描述】:

我正在实现一个 LZW 压缩/解压缩实用程序库,并且需要以我正在使用的方式返回压缩输出:

using ByteSequence = std::vector<std::uint8_t>

压缩器的输出格式将包括算法找到的各种序列在压缩器字典中的位置。例如,输出中的 16 位位置如下所示:

std::vector<std::uint16_t> pos{123, 385, /* ... */};

但是,输出必须是ByteSequence,并且需要在架构之间移植。我目前将pos 向量转换为所需格式的操作是:

for (auto p : pos)
{
  std::uint8_t *bytes = (std::uint8_t *) &p;
  output.push_back(bytes[0]);
  output.push_back(bytes[1]);
}

这可行,但前提是每个密钥都是 16 位的,老实说,这对我来说似乎是一个廉价的把戏。

我应该如何以更好、更清洁的方式做到这一点?谢谢!

【问题讨论】:

  • “需要在架构之间移植” - 不是通过代码做的事情,它不是。在 best 时,这将根据编码器上存在的字节序发出不同的字节流。使用移位和掩码将每个八位组从uint16_t 中拉出;先高后低,并确保您的解码器知道该决定。
  • @WhozCraig 我明白你的意思,我也是这么想的。但是,对于大于 2 个字节的键,我还应该以相同的方式提取字节吗?
  • 和你这里展示的一样吗?还是像我描述的那样逐字节自上而下?对前者不,对后者?

标签: c++ c++17


【解决方案1】:

提取字节的方式是未定义的行为。 C++ 标准 [basic.lval] 内容如下:

如果程序尝试通过非下列类型之一的左值访问对象的存储值,则行为未定义:

。 . .

  • charunsigned charstd::byte 类型。

std::uint8_t 不在此列表中,AFAIK there is no guarantee 认为 std::uint8_tunsigned char 是同一类型。


转换函数可能如下所示:

template<typename T>
void convert_forward(const std::vector<T>& in, std::vector<std::uint8_t>& out) {
    out.reserve(out.size() + in.size() * sizeof(T));
    for (const T& i : in) {
        std::uint8_t buff[sizeof(T)];
        std::memcpy(buff, &i, sizeof(T));
        std::copy(std::begin(buff), std::end(buff), std::back_inserter(out));
    }
}

没有back_inserter的替代实现:

template<typename T>
void convert_forward(const std::vector<T>& in, std::vector<std::uint8_t>& out) {
    const auto old_size = out.size();
    out.resize(old_size + in.size() * sizeof(T));
    auto dest = out.data() + old_size;
    for (const T& i : in) {
        std::memcpy(dest, &i, sizeof(T));
        dest += sizeof(T);
    }
}

注意字节顺序。在前向转换或后向转换中都应考虑到这一点。

【讨论】:

  • 感谢您的回答!你能告诉我为什么这是未定义的行为吗?我看到this 回答指向这个确切的代码片段来检测字节顺序。另外,您能否详细说明一下应考虑字节顺序?我知道当数据在一个字节序不同的系统上读取时会发生什么,但我不确定什么是最好的补偿方式。
  • 我明白你在说什么。我将调整我的实现以在适当的地方使用std::byte。非常感谢!
  • @Victor,为了补偿字节顺序,您通常在将字节解释为值之前手动反转字节(如果字节顺序不同)。
【解决方案2】:

这应该是可移植的,虽然可能不如直接字节操作那么有效:

template<class T>
void number2bytes(std::vector<uint8_t>& bytes, T x)
{
    static_assert(std::is_integral<T>::value, "Integral required.");
    for (size_t i = 0; i < sizeof(T); ++i)
    {
        bytes.push_back(x & 0xFF);
        x >>= 8;
    }
}

添加static_assert 是为了防止意外传递一些奇怪的非数字类型重载&amp;&gt;&gt;=

【讨论】:

    猜你喜欢
    • 2014-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-31
    相关资源
    最近更新 更多