memset(data,0,1500); // This might not be correct but it works fine
It isn’t correct, and it doesn’t “work fine”. This is Undefined Behaviour, and you’re making the common mistake of assuming that if it compiles, and your computer doesn’t instantly catch fire, everything is fine.
It really isn’t.
I’ve done something which I wasn’t supposed to do!
Yes, you have. You took a pointer to a std::string
, a non-trivial object with its own state and behaviour, asked it for the address of some memory it controls, and cast that to void*
.
There’s no reason to do that, you should very rarely ever see void*
in C++ code, and seeing C-style casts to any type is pretty worrying.
Don’t take void*
pointers into objects with state and behaviour like std::string
until you understand what you’re doing and why this is wrong. Then, when that day comes, you still won’t do it because you’ll know better.
We can look at the first problem in some fine detail, if it helps:
(void *)l_str.c_str()
- what does
c_str()
return? A pointer to some memory owned byl_str
- where is this memory? No idea, that’s
l_str
‘s business. If this standard library implementation uses the small string optimization, it may be inside thel_str
object. If not, it may be dynamically allocated. - how much memory is allocated at this location? No idea, that’s
l_str
‘s business. All we can say for sure is that there is at least one legally-addressable char (l_str.c_str()[0] == '\0'
) and that it’s legal to use the addressl_str.c_str()+1
(but only as a one-past-the-end pointer, so you can’t dereference it)
So, the statement
strRet((void *)l_str.c_str());
passes strRet
a pointer to a location containing one or more addressable chars, of which the first is zero. That’s everything we can say about it.
Now let’s look again at the problematic line
memset(data,0,1500); // This might not be correct but it works fine
why would we expect there to be 1500 chars at this location? If you’d documented strRet
as requiring a buffer of at least 1500 allocated chars, would it look reasonable to actually pass l_str.c_str()
when you know l_str
has just been default constructed as an empty string? It’s not like you asked l_str
to allocate that storage for you.
You could start to make this work by giving l_str
a chance to allocate the memory you intend to write, by calling
l_str.reserve(1500);
before calling strRet
. This still won’t notify l_str
that you filled it with 'a'
s though, because you did that by changing the raw memory behind its back.
If you want this to work correctly, you could replace the entirety of strRet
with
std::string l_str(1500, 'a');
or, if you want to change an existing string correctly, with
void strRet(std::string& out) {
// this just speeds it up, since we know the size in advance
out.reserve(1500);
// this is in case the string wasn't already empty
out.clear();
// and this actually does the work
std::fill_n(std::back_inserter(out), 1500, 'a');
}
5
solved Manipulating std::string