Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
241 views
in Technique[技术] by (71.8m points)

c++ - reading/writing from ostringstream changes original data

Inside a test, I write a given value to an ostringstream. After that, I try to evaluate if the correct value was written. However, it seems that the original value I wrote into the stream is changed later on while reading from it.

I reduced my problem to the following code:

#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);
}

Here data seems to be changed (to 0) by std::copy().

I also put the code on godbolt: https://godbolt.org/z/bcb4PE

Even more strange, if I change uint16_t data{ 123 }; to const uint16_t data{ 123 };, everything is fine.

Seems like I am missing some insights on the mechanics of std::copy().

question from:https://stackoverflow.com/questions/65905940/reading-writing-from-ostringstream-changes-original-data

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

std::copy() does not do what you think it does in this situation. It is just a wrapper for a logical loop that copies individual elements from one container to another. You are essentially telling it to copy 2 individual chars from data_str into a uint16_t[2] array, except that you don't actually have such as array, so you have undefined behavior and are corrupting stack memory.

This statement:

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

Is basically doing this:

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

Which is basically just the equivalent of doing this, in your example:

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

It is assigning the 1st byte to the whole uint16_t (which is why you see the value change), and then it is assigning the 2nd byte to the next whole uint16_t (corrupting the stack).

For what you are attempting, use std::memcpy() instead, eg:

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

Otherwise, if you really want to use std::copy(), you need to make sure it knows to copy individual bytes into the destination, not whole uint16_ts, eg:

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

Since both input and output are trivial types, that should get optimized down to an appropriate std::memcpy()-equivalent copy.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...