【问题标题】:reading/writing from ostringstream changes original data从 ostringstream 读取/写入会更改原始数据
【发布时间】:2021-01-26 17:11:22
【问题描述】:

在测试中,我将给定值写入ostringstream。之后,我尝试评估是否写入了正确的值。但是,我写入流中的原始值似乎在稍后读取时发生了更改。

我将问题简化为以下代码:

#include <sstream>
#include <cassert>
#include <iostream>

int main()
{
    std::ostringstream os;
    uint16_t data{ 123 };
    os.write(reinterpret_cast<const char*>(&data), sizeof(uint16_t));

    uint16_t data_returned;
    std::string data_str(os.str());
  
    std::copy(data_str.begin(), data_str.end(), &data_returned);

    assert(data == 123); // <- not true
    assert(data_returned == 123);
}

这里的data 似乎被std::copy() 更改(为0)。

我也把代码放在godbolt上:https://godbolt.org/z/bcb4PE

更奇怪的是,如果我把uint16_t data{ 123 };改成const uint16_t data{ 123 };,一切都很好。

似乎我缺少对 std::copy() 机制的一些见解。

【问题讨论】:

    标签: c++ ostringstream


    【解决方案1】:

    std::copy() 在这种情况下不会做你认为它会做的事情。它只是将 individual 元素从一个容器复制到另一个容器的逻辑循环的包装器。您实际上是在告诉它将 2 个单独的 charsdata_str 复制到 uint16_t[2] 数组中,除了您实际上没有这样的数组,因此您有 未定义的行为并且正在破坏堆栈内存。

    此声明:

    std::copy(data_str.begin(), data_str.end(), &data_returned);
    

    基本上是这样做的:

    std::string::iterator iter = data_str.begin(), end = data_str.end();
    uint16_t *dest = &data_returned;
    while (iter != end) {
        *dest++ = *iter++;
    }
    

    在您的示例中,这基本上相当于这样做:

    uint16_t *dest = &data_returned;
    dest[0] = static_cast<uint16_t>(data_str[0]);
    dest[1] = static_cast<uint16_t>(data_str[1]);
    

    它将第一个 byte 分配给整个 uint16_t(这就是您看到值变化的原因),然后将第二个 byte 分配给下一个整个uint16_t(破坏堆栈)。

    对于您正在尝试的内容,请改用std::memcpy(),例如:

    std::memcpy(&data_returned, data_str.c_str(), sizeof(data_returned));
    

    否则,如果你真的想使用std::copy(),你需要确保它知道将单个字节复制到目的地,而不是整个uint16_ts,例如:

    std::copy(data_str.begin(), data_str.end(), reinterpret_cast<char*>(&data_returned));
    

    由于输入和输出都是普通类型,因此应将其优化为适当的 std::memcpy() 等效副本。

    【讨论】:

      【解决方案2】:

      要以这种方式使用std::copy(),您需要将&amp;data_returned 转换为char*,否则std::copy() 会将其视为uint16_t* 并填充两个uint16_ts 而不是两个chars 你的目标。这使您的程序具有undefined behavior,这就是您在data 中看到更改值的原因。该程序也可能崩溃或做了一些完全不同的事情。

      std::copy(data_str.begin(), data_str.end(), reinterpret_cast<char*>(&data_returned));
      

      这是您的程序编译时使用了两个额外的选项 (-ggdb -fsanitize=address),以便将来更容易发现此类问题:Demo

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-02
        • 2010-09-07
        • 2020-03-07
        • 1970-01-01
        • 2012-02-11
        • 1970-01-01
        相关资源
        最近更新 更多