Are there downsides to using std::string as a buffer?Meaning of acronym SSO in the context of std::stringBenefits of vector<char> over string?Understanding GCC 5's _GLIBCXX_USE_CXX11_ABI or the new ABIHow to create a std::string directly from a char* array without copying?Can you avoid using temporary buffers when using std::string to interact with C style APIs?Specific behaviour of std::string on visual studio?Google protocol buffers and use of std::string for arbitrary binary dataCan I safely use std::string for binary data in C++11?Using a shared_ptr<string> into an unordered_set<string>Sockets: getting UDP data into a word-aligned buffer using recvfrom without memcpy?Why std::string hasn't const char* cast operator in C++11?std::string how to do a constructor like std::string(char* allocated_outside)Giving char* to std::string for management (to eventually free memory)
Why did the people of Zion never find evidence of the previous cycles?
How can I determine if two vertices on a polygon are consecutive?
Continents with simplex noise
windows 10 computer waking up from sleep
Starting a fire in a cold planet that was full of flammable gas
What's the earliest a champion can have a focus pool of 3?
70 million rials in usurious money
What happens to a Bladesinger reincarnated as a Human?
How is warfare affected when armor has (temporally) outpaced guns? How can guns compete?
Meaning of "in arms"
Why buy a first class ticket on Southern trains?
What happens on pressing Ctrl-T in a terminal when a program is running?
Reviewer wants me to do massive amount of work, the result would be a different article. Should I tell that to the editor?
What game(s) does Michael play in Mind Field S2E4?
Will there be a vote in the Commons to decide the fate of Johnson's deal?
Asking my PhD advisor for a postdoc position. Is it bad to appear desperate?
Is it bizarre that a professor asks every student for a 3 inch by 5 inch photograph?
How does the Gameboy Link Cable work?
I'm being drafted to the military but I have the option to refuse (legally), would it be immoral for me to serve?
What's the physical meaning of the standard base vectors?
Command which removes data left side of ";" (semicolon) on each row
Why past tense of vomit generally spelled 'vomited' rather than 'vomitted'?
Leaving passport in the hotel room
Travel with Expired Greek Passport from UK to Greece and Return
Are there downsides to using std::string as a buffer?
Meaning of acronym SSO in the context of std::stringBenefits of vector<char> over string?Understanding GCC 5's _GLIBCXX_USE_CXX11_ABI or the new ABIHow to create a std::string directly from a char* array without copying?Can you avoid using temporary buffers when using std::string to interact with C style APIs?Specific behaviour of std::string on visual studio?Google protocol buffers and use of std::string for arbitrary binary dataCan I safely use std::string for binary data in C++11?Using a shared_ptr<string> into an unordered_set<string>Sockets: getting UDP data into a word-aligned buffer using recvfrom without memcpy?Why std::string hasn't const char* cast operator in C++11?std::string how to do a constructor like std::string(char* allocated_outside)Giving char* to std::string for management (to eventually free memory)
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty
margin-bottom:0;
I have recently seen a colleague of mine using std::string
as a buffer:
std::string receive_data(const Receiver& receiver)
std::string buff;
int size = receiver.size();
if (size > 0)
buff.resize(size);
const char* dst_ptr = buff.data();
const char* src_ptr = receiver.data();
memcpy((char*) dst_ptr, src_ptr, size);
return buff;
I guess this guy wants to take advantage of auto destruction of the returned string so he needs not worry about freeing of the allocated buffer.
This looks a bit strange to me since according to cplusplus.com the data()
method returns a const char*
pointing to a buffer internally managed by the string:
const char* data() const noexcept;
Memcpy-ing to a const char pointer? AFAIK this does no harm as long as we know what we do, but have I missed something? Is this dangerous?
c++ c++11 stdstring
|
show 11 more comments
I have recently seen a colleague of mine using std::string
as a buffer:
std::string receive_data(const Receiver& receiver)
std::string buff;
int size = receiver.size();
if (size > 0)
buff.resize(size);
const char* dst_ptr = buff.data();
const char* src_ptr = receiver.data();
memcpy((char*) dst_ptr, src_ptr, size);
return buff;
I guess this guy wants to take advantage of auto destruction of the returned string so he needs not worry about freeing of the allocated buffer.
This looks a bit strange to me since according to cplusplus.com the data()
method returns a const char*
pointing to a buffer internally managed by the string:
const char* data() const noexcept;
Memcpy-ing to a const char pointer? AFAIK this does no harm as long as we know what we do, but have I missed something? Is this dangerous?
c++ c++11 stdstring
28
With C++17data()
has an overload returning a pointer to non-const qualified char.
– VTT
Jun 3 at 7:35
38
...which is mentioned by cppreference. cplusplus.com is not the best source.
– HolyBlackCat
Jun 3 at 7:36
11
I think that the operation of casting fromconst char*
tochar*
by itself implies some sort of hazard in your program. Ifdst_ptr
points to a read-only memory block, then you should not attempt to write into that block using this pointer.
– goodvibration
Jun 3 at 7:37
6
Whenever you see code using C-style casts (like e.g.(char*) dst_ptr
) you should take that as a red flag.
– Some programmer dude
Jun 3 at 7:38
9
I think this question is mostly opinion-based. IMO usingstd::string
as a buffer is fine if you know you're receiving text data. If you're receiving binary datastd::vector<char>
is probably a better choice.
– Miles Budnek
Jun 3 at 7:42
|
show 11 more comments
I have recently seen a colleague of mine using std::string
as a buffer:
std::string receive_data(const Receiver& receiver)
std::string buff;
int size = receiver.size();
if (size > 0)
buff.resize(size);
const char* dst_ptr = buff.data();
const char* src_ptr = receiver.data();
memcpy((char*) dst_ptr, src_ptr, size);
return buff;
I guess this guy wants to take advantage of auto destruction of the returned string so he needs not worry about freeing of the allocated buffer.
This looks a bit strange to me since according to cplusplus.com the data()
method returns a const char*
pointing to a buffer internally managed by the string:
const char* data() const noexcept;
Memcpy-ing to a const char pointer? AFAIK this does no harm as long as we know what we do, but have I missed something? Is this dangerous?
c++ c++11 stdstring
I have recently seen a colleague of mine using std::string
as a buffer:
std::string receive_data(const Receiver& receiver)
std::string buff;
int size = receiver.size();
if (size > 0)
buff.resize(size);
const char* dst_ptr = buff.data();
const char* src_ptr = receiver.data();
memcpy((char*) dst_ptr, src_ptr, size);
return buff;
I guess this guy wants to take advantage of auto destruction of the returned string so he needs not worry about freeing of the allocated buffer.
This looks a bit strange to me since according to cplusplus.com the data()
method returns a const char*
pointing to a buffer internally managed by the string:
const char* data() const noexcept;
Memcpy-ing to a const char pointer? AFAIK this does no harm as long as we know what we do, but have I missed something? Is this dangerous?
c++ c++11 stdstring
c++ c++11 stdstring
edited Jun 3 at 18:02
TylerH
16.7k10 gold badges57 silver badges72 bronze badges
16.7k10 gold badges57 silver badges72 bronze badges
asked Jun 3 at 7:33
duong_dajgjaduong_dajgja
2,1861 gold badge19 silver badges41 bronze badges
2,1861 gold badge19 silver badges41 bronze badges
28
With C++17data()
has an overload returning a pointer to non-const qualified char.
– VTT
Jun 3 at 7:35
38
...which is mentioned by cppreference. cplusplus.com is not the best source.
– HolyBlackCat
Jun 3 at 7:36
11
I think that the operation of casting fromconst char*
tochar*
by itself implies some sort of hazard in your program. Ifdst_ptr
points to a read-only memory block, then you should not attempt to write into that block using this pointer.
– goodvibration
Jun 3 at 7:37
6
Whenever you see code using C-style casts (like e.g.(char*) dst_ptr
) you should take that as a red flag.
– Some programmer dude
Jun 3 at 7:38
9
I think this question is mostly opinion-based. IMO usingstd::string
as a buffer is fine if you know you're receiving text data. If you're receiving binary datastd::vector<char>
is probably a better choice.
– Miles Budnek
Jun 3 at 7:42
|
show 11 more comments
28
With C++17data()
has an overload returning a pointer to non-const qualified char.
– VTT
Jun 3 at 7:35
38
...which is mentioned by cppreference. cplusplus.com is not the best source.
– HolyBlackCat
Jun 3 at 7:36
11
I think that the operation of casting fromconst char*
tochar*
by itself implies some sort of hazard in your program. Ifdst_ptr
points to a read-only memory block, then you should not attempt to write into that block using this pointer.
– goodvibration
Jun 3 at 7:37
6
Whenever you see code using C-style casts (like e.g.(char*) dst_ptr
) you should take that as a red flag.
– Some programmer dude
Jun 3 at 7:38
9
I think this question is mostly opinion-based. IMO usingstd::string
as a buffer is fine if you know you're receiving text data. If you're receiving binary datastd::vector<char>
is probably a better choice.
– Miles Budnek
Jun 3 at 7:42
28
28
With C++17
data()
has an overload returning a pointer to non-const qualified char.– VTT
Jun 3 at 7:35
With C++17
data()
has an overload returning a pointer to non-const qualified char.– VTT
Jun 3 at 7:35
38
38
...which is mentioned by cppreference. cplusplus.com is not the best source.
– HolyBlackCat
Jun 3 at 7:36
...which is mentioned by cppreference. cplusplus.com is not the best source.
– HolyBlackCat
Jun 3 at 7:36
11
11
I think that the operation of casting from
const char*
to char*
by itself implies some sort of hazard in your program. If dst_ptr
points to a read-only memory block, then you should not attempt to write into that block using this pointer.– goodvibration
Jun 3 at 7:37
I think that the operation of casting from
const char*
to char*
by itself implies some sort of hazard in your program. If dst_ptr
points to a read-only memory block, then you should not attempt to write into that block using this pointer.– goodvibration
Jun 3 at 7:37
6
6
Whenever you see code using C-style casts (like e.g.
(char*) dst_ptr
) you should take that as a red flag.– Some programmer dude
Jun 3 at 7:38
Whenever you see code using C-style casts (like e.g.
(char*) dst_ptr
) you should take that as a red flag.– Some programmer dude
Jun 3 at 7:38
9
9
I think this question is mostly opinion-based. IMO using
std::string
as a buffer is fine if you know you're receiving text data. If you're receiving binary data std::vector<char>
is probably a better choice.– Miles Budnek
Jun 3 at 7:42
I think this question is mostly opinion-based. IMO using
std::string
as a buffer is fine if you know you're receiving text data. If you're receiving binary data std::vector<char>
is probably a better choice.– Miles Budnek
Jun 3 at 7:42
|
show 11 more comments
7 Answers
7
active
oldest
votes
Don't use std::string
as a buffer.
It is bad practice to use std::string
as a buffer, for several reasons (listed in no particular order):
std::string
was not intended for use as a buffer; you would need to double-check the description of the class to make sure there are no "gotchas" which would prevent certain usage patterns (or make them trigger undefined behavior).- As a concrete example: Before C++17, you can't even write through the pointer you get with
data()
- it'sconst Tchar *
; so your code would cause undefined behavior. (But&(str[0])
,&(str.front())
, or&(*(str.begin()))
would work.) - Using
std::string
s for buffers is confusing to readers of the implementation, who assume you would be usingstd::string
for, well, strings. In other words, doing so breaks the Principle of Least Astonishment. - Worse yet, it's confusing for whoever might use this function - they too may think what you're returning is a string, i.e. valid human-readable text.
std::unique_ptr
would be fine for your case, or evenstd::vector
. In C++17, you can usestd::byte
for the element type, too. A more sophisticated option is a class with an SSO-like feature, e.g. Boost'ssmall_vector
(thank you, @gast128, for mentioning it).- (Minor point:) libstdc++ had to change its ABI for
std::string
to conform to the C++11 standard, which in some cases (which by now are rather unlikely), you might run into some linkage or runtime issues that you wouldn't with a different type for your buffer.
Also, your code may make two instead of one heap allocations (implementation dependent): Once upon string construction and another when resize()
ing. But that in itself is not really a reason to avoid std::string
, since you can avoid the double allocation using the construction in @Jarod42's answer.
3
Can you explain what you mean in the first bullet point? What "gotchas" ?
– M.M
Jun 3 at 8:08
2
And withstd::string_view
, you can define readable portions of the buffer.
– Zereges
Jun 3 at 8:09
16
@M.M For example the terminatingcharacter which
std::string
guarantees to be present. Would you know off-hand whether it is included in the valid range ofdata()
? Overwriting it is UB! It needs a few extra mental cycles (and possibly reference lookups) to verify this function does not have UB from that.
– Max Langhof
Jun 3 at 8:13
3
@M.M: The point is - you don't intuitively know! I wasn't even aware of what MaxLanghof wrote in his comment.
– einpoklum
Jun 3 at 9:56
5
+1 forstd::byte
, unbelievable I haven't heard of it before! It's crazy how often one looks into some C++ reference every day and still new things keep popping up...
– andreee
Jun 3 at 20:08
|
show 9 more comments
You can completely avoid a manual memcpy
by calling the appropriate constructor:
std::string receive_data(const Receiver& receiver)
return receiver.data(), receiver.size();
That even handles in a string.
BTW, unless content is actually text, I would prefer std::vector<std::byte>
(or equivalent).
4
As original buffer data should not be notconst
, you should not have UB.
– Jarod42
Jun 3 at 7:56
14
And by then a good programmer goes: "Why the hell do I need a separate one-liner conversion function? Not only that, but this function named 'receive' doesn't actually perform any 'receiving'. Delete!"
– screwnut
Jun 3 at 8:32
6
@screwnut: To be fair, it is theoretically possible thatreceiver.data()
waits on the reception to happen, or does something else beyond returning a member pointer.
– einpoklum
Jun 3 at 22:53
9
@screwnut: Then I'm a bad programmer; I'll keep the conversion in a separate function, even if it's a one liner, because I appreciate abstraction and do not like to repeat myself. If I later need to add some checks, some logging, etc... the function is right here and I don't have to hunt around the codebase for all instances of conversion.
– Matthieu M.
Jun 4 at 6:58
5
@screwnut: Prefer non-member non-friend functions.
– Matthieu M.
Jun 4 at 10:57
|
show 5 more comments
Memcpy-ing to a const char pointer? AFAIK this does no harm as long as we know what we do, but is this good behavior and why?
The current code may have undefined behavior, depending on the C++ version. To avoid undefined behavior in C++14 and below take the address of the first element. It yields a non-const pointer:
buff.resize(size);
memcpy(&buff[0], &receiver[0], size);
I have recently seen a colleague of mine using
std::string
as a buffer...
That was somewhat common in older code, especially circa C++03. There are several benefits and downsides to using a string like that. Depending on what you are doing with the code, std::vector
can be a bit anemic, and you sometimes used a string instead and accepted the extra overhead of char_traits
.
For example, std::string
is usually a faster container than std::vector
on append, and you can't return std::vector
from a function. (Or you could not do so in practice in C++98 because C++98 required the vector to be constructed in the function and copied out). Additionally, std::string
allowed you to search with a richer assortment of member functions, like find_first_of
and find_first_not_of
. That was convenient when searching though arrays of bytes.
I think what you really want/need is SGI's Rope class, but it never made it into the STL. It looks like GCC's libstdc++ may provide it.
There a lengthy discussion about this being legal in C++14 and below:
const char* dst_ptr = buff.data();
const char* src_ptr = receiver.data();
memcpy((char*) dst_ptr, src_ptr, size);
I know for certain it is not safe in GCC. I once did something like this in some self tests and it resulted in a segfault:
std::string buff("A");
...
char* ptr = (char*)buff.data();
size_t len = buff.size();
ptr[0] ^= 1; // tamper with byte
bool tampered = HMAC(key, ptr, len, mac);
GCC put the single byte 'A'
in register AL
. The high 3-bytes were garbage, so the 32-bit register was 0xXXXXXX41
. When I dereferenced at ptr[0]
, GCC dereferenced a garbage address 0xXXXXXX41
.
The two take-aways for me were, don't write half-ass self tests, and don't try to make data()
a non-const pointer.
7
Preferstd::copy
for type-safety. It won't be slower.
– Lightness Races in Orbit
Jun 3 at 17:07
3
Seems like the only answer directly answering the question.
– Keith
Jun 4 at 0:59
2
"you can't returnstd::vector
from a function. (Or you could not do so back in C++98 or C++03)" is wrong.
– Ben Voigt
Jun 4 at 2:38
3
And it has never been a legal optimization for a compiler to confuse an address with stored content.buff.data()
cannot be a register containing'A'
, it must be an address.
– Ben Voigt
Jun 4 at 2:40
2
@jww: Indeed, although you could avoid that copy with NRVO and aswap()
call. But the copy would also be required ofstd::string
. Small string optimization could make it a little better. I think some implementations tried to solve this using copy-on-write (forstring
, never allowed forvector
) but even in C++98 and C++03 there were some specifications onstd::string
that couldn't be reasonable met by COW. Of course rvalue references and moves solved it neatly.
– Ben Voigt
Jun 4 at 3:01
|
show 3 more comments
From C++17, data
can return a non const char *
.
Draft n4659 declares at [string.accessors]:
const charT* c_str() const noexcept;
const charT* data() const noexcept;
....
charT* data() noexcept;
8
@SergeBallesta - Removing a const qualifier is not UB. Modifying a const object is UB. The object in question is not const.
– StoryTeller
Jun 3 at 7:59
6
@SergeBallesta - Seriously? And how do you reconcile that with&str[0]
being a non-const pointer to the very same buffer? The object is guaranteed not to be const. The core rules of the language still apply, even to pointers returned from library types, ergo, no UB.
– StoryTeller
Jun 3 at 8:05
4
@Jarod42: I agree that I am nitpicking here, but the library could expect the buffer not be be changed, and later reuse a cached version. Coming for old K&R C I am now frightened by optimizing compilers and very cautious for constness and strict aliasing.
– Serge Ballesta
Jun 3 at 8:07
8
@StoryTeller Serge is correct that "Modifying the character array accessed through the const overload of data has undefined behavior." according to cppreference and the standard.
– Max Langhof
Jun 3 at 8:16
4
(For completeness, here is the C++11 wording: timsong-cpp.github.io/cppwp/n3337/string.ops#string.accessors-3)
– Max Langhof
Jun 3 at 8:22
|
show 13 more comments
The code is unnecessary, considering that
std::string receive_data(const Receiver& receiver)
std::string buff;
int size = receiver.size();
if (size > 0)
buff.assign(receiver.data(), size);
return buff;
will do exactly the same.
2
You can cut even more code; theif
is also unnecessary.assign
will be a no-op then. But continue to unnecessary remove code, and you end up with Jarod42's answer. None of these lines are necessary, asstd::string
already has an appropriate constructor.
– MSalters
Jun 4 at 11:32
@MSalters I prefer not to assume things that are not given. What ifreceiver.size()
can return negative values?
– Kit.
Jun 5 at 14:02
That would be rather unexpected, given that sizes are typically asize_t
and therefore unsigned. That does show a possible problem with your code: it might suffer from signed integer overflow, which is undefined behavior. And that's on a code path which handles input, so this may constitute an externally exploitable vulnerability.
– MSalters
Jun 6 at 8:53
@MSalters True, Jarod42's changes could introduce an externally exploitable vulnerability. They could also introduce crashes ifreceiver.data()
is UB whenreceiver.size()
is zero.
– Kit.
Jun 7 at 7:41
add a comment
|
The big optimization opportunity I would investigate here is: Receiver
appears to be some kind of container that supports .data()
and .size()
. If you can consume it, and pass it in as a rvalue reference Receiver&&
, you might be able to use move semantics without making any copies at all! If it’s got an iterator interface, you could use those for range-based constructors or std::move()
from <algorithm>
.
In C++17 (as Serge Ballesta and others have mentioned), std::string::data()
returns a pointer to non-const data. A std::string
has been guaranteed to store all its data contiguously for years.
The code as written smells a bit, although it’s not really the programmer’s fault: those hacks were necessary at the time. Today, you should at least change the type of dst_ptr
from const char*
to char*
and remove the cast in the first argument to memcpy()
. You could also reserve()
a number of bytes for the buffer and then use a STL function to move the data.
As others have mentioned, a std::vector
or std::unique_ptr
would be a more natural data structure to use here.
add a comment
|
One downside is performance.
The .resize method will default-initialize all the new byte locations to 0.
That initialization is unnecessary if you're then going to overwrite the 0s with other data.
add a comment
|
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/4.0/"u003ecc by-sa 4.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f56422913%2fare-there-downsides-to-using-stdstring-as-a-buffer%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
7 Answers
7
active
oldest
votes
7 Answers
7
active
oldest
votes
active
oldest
votes
active
oldest
votes
Don't use std::string
as a buffer.
It is bad practice to use std::string
as a buffer, for several reasons (listed in no particular order):
std::string
was not intended for use as a buffer; you would need to double-check the description of the class to make sure there are no "gotchas" which would prevent certain usage patterns (or make them trigger undefined behavior).- As a concrete example: Before C++17, you can't even write through the pointer you get with
data()
- it'sconst Tchar *
; so your code would cause undefined behavior. (But&(str[0])
,&(str.front())
, or&(*(str.begin()))
would work.) - Using
std::string
s for buffers is confusing to readers of the implementation, who assume you would be usingstd::string
for, well, strings. In other words, doing so breaks the Principle of Least Astonishment. - Worse yet, it's confusing for whoever might use this function - they too may think what you're returning is a string, i.e. valid human-readable text.
std::unique_ptr
would be fine for your case, or evenstd::vector
. In C++17, you can usestd::byte
for the element type, too. A more sophisticated option is a class with an SSO-like feature, e.g. Boost'ssmall_vector
(thank you, @gast128, for mentioning it).- (Minor point:) libstdc++ had to change its ABI for
std::string
to conform to the C++11 standard, which in some cases (which by now are rather unlikely), you might run into some linkage or runtime issues that you wouldn't with a different type for your buffer.
Also, your code may make two instead of one heap allocations (implementation dependent): Once upon string construction and another when resize()
ing. But that in itself is not really a reason to avoid std::string
, since you can avoid the double allocation using the construction in @Jarod42's answer.
3
Can you explain what you mean in the first bullet point? What "gotchas" ?
– M.M
Jun 3 at 8:08
2
And withstd::string_view
, you can define readable portions of the buffer.
– Zereges
Jun 3 at 8:09
16
@M.M For example the terminatingcharacter which
std::string
guarantees to be present. Would you know off-hand whether it is included in the valid range ofdata()
? Overwriting it is UB! It needs a few extra mental cycles (and possibly reference lookups) to verify this function does not have UB from that.
– Max Langhof
Jun 3 at 8:13
3
@M.M: The point is - you don't intuitively know! I wasn't even aware of what MaxLanghof wrote in his comment.
– einpoklum
Jun 3 at 9:56
5
+1 forstd::byte
, unbelievable I haven't heard of it before! It's crazy how often one looks into some C++ reference every day and still new things keep popping up...
– andreee
Jun 3 at 20:08
|
show 9 more comments
Don't use std::string
as a buffer.
It is bad practice to use std::string
as a buffer, for several reasons (listed in no particular order):
std::string
was not intended for use as a buffer; you would need to double-check the description of the class to make sure there are no "gotchas" which would prevent certain usage patterns (or make them trigger undefined behavior).- As a concrete example: Before C++17, you can't even write through the pointer you get with
data()
- it'sconst Tchar *
; so your code would cause undefined behavior. (But&(str[0])
,&(str.front())
, or&(*(str.begin()))
would work.) - Using
std::string
s for buffers is confusing to readers of the implementation, who assume you would be usingstd::string
for, well, strings. In other words, doing so breaks the Principle of Least Astonishment. - Worse yet, it's confusing for whoever might use this function - they too may think what you're returning is a string, i.e. valid human-readable text.
std::unique_ptr
would be fine for your case, or evenstd::vector
. In C++17, you can usestd::byte
for the element type, too. A more sophisticated option is a class with an SSO-like feature, e.g. Boost'ssmall_vector
(thank you, @gast128, for mentioning it).- (Minor point:) libstdc++ had to change its ABI for
std::string
to conform to the C++11 standard, which in some cases (which by now are rather unlikely), you might run into some linkage or runtime issues that you wouldn't with a different type for your buffer.
Also, your code may make two instead of one heap allocations (implementation dependent): Once upon string construction and another when resize()
ing. But that in itself is not really a reason to avoid std::string
, since you can avoid the double allocation using the construction in @Jarod42's answer.
3
Can you explain what you mean in the first bullet point? What "gotchas" ?
– M.M
Jun 3 at 8:08
2
And withstd::string_view
, you can define readable portions of the buffer.
– Zereges
Jun 3 at 8:09
16
@M.M For example the terminatingcharacter which
std::string
guarantees to be present. Would you know off-hand whether it is included in the valid range ofdata()
? Overwriting it is UB! It needs a few extra mental cycles (and possibly reference lookups) to verify this function does not have UB from that.
– Max Langhof
Jun 3 at 8:13
3
@M.M: The point is - you don't intuitively know! I wasn't even aware of what MaxLanghof wrote in his comment.
– einpoklum
Jun 3 at 9:56
5
+1 forstd::byte
, unbelievable I haven't heard of it before! It's crazy how often one looks into some C++ reference every day and still new things keep popping up...
– andreee
Jun 3 at 20:08
|
show 9 more comments
Don't use std::string
as a buffer.
It is bad practice to use std::string
as a buffer, for several reasons (listed in no particular order):
std::string
was not intended for use as a buffer; you would need to double-check the description of the class to make sure there are no "gotchas" which would prevent certain usage patterns (or make them trigger undefined behavior).- As a concrete example: Before C++17, you can't even write through the pointer you get with
data()
- it'sconst Tchar *
; so your code would cause undefined behavior. (But&(str[0])
,&(str.front())
, or&(*(str.begin()))
would work.) - Using
std::string
s for buffers is confusing to readers of the implementation, who assume you would be usingstd::string
for, well, strings. In other words, doing so breaks the Principle of Least Astonishment. - Worse yet, it's confusing for whoever might use this function - they too may think what you're returning is a string, i.e. valid human-readable text.
std::unique_ptr
would be fine for your case, or evenstd::vector
. In C++17, you can usestd::byte
for the element type, too. A more sophisticated option is a class with an SSO-like feature, e.g. Boost'ssmall_vector
(thank you, @gast128, for mentioning it).- (Minor point:) libstdc++ had to change its ABI for
std::string
to conform to the C++11 standard, which in some cases (which by now are rather unlikely), you might run into some linkage or runtime issues that you wouldn't with a different type for your buffer.
Also, your code may make two instead of one heap allocations (implementation dependent): Once upon string construction and another when resize()
ing. But that in itself is not really a reason to avoid std::string
, since you can avoid the double allocation using the construction in @Jarod42's answer.
Don't use std::string
as a buffer.
It is bad practice to use std::string
as a buffer, for several reasons (listed in no particular order):
std::string
was not intended for use as a buffer; you would need to double-check the description of the class to make sure there are no "gotchas" which would prevent certain usage patterns (or make them trigger undefined behavior).- As a concrete example: Before C++17, you can't even write through the pointer you get with
data()
- it'sconst Tchar *
; so your code would cause undefined behavior. (But&(str[0])
,&(str.front())
, or&(*(str.begin()))
would work.) - Using
std::string
s for buffers is confusing to readers of the implementation, who assume you would be usingstd::string
for, well, strings. In other words, doing so breaks the Principle of Least Astonishment. - Worse yet, it's confusing for whoever might use this function - they too may think what you're returning is a string, i.e. valid human-readable text.
std::unique_ptr
would be fine for your case, or evenstd::vector
. In C++17, you can usestd::byte
for the element type, too. A more sophisticated option is a class with an SSO-like feature, e.g. Boost'ssmall_vector
(thank you, @gast128, for mentioning it).- (Minor point:) libstdc++ had to change its ABI for
std::string
to conform to the C++11 standard, which in some cases (which by now are rather unlikely), you might run into some linkage or runtime issues that you wouldn't with a different type for your buffer.
Also, your code may make two instead of one heap allocations (implementation dependent): Once upon string construction and another when resize()
ing. But that in itself is not really a reason to avoid std::string
, since you can avoid the double allocation using the construction in @Jarod42's answer.
edited Jul 4 at 19:32
answered Jun 3 at 7:56
einpoklumeinpoklum
45k30 gold badges160 silver badges314 bronze badges
45k30 gold badges160 silver badges314 bronze badges
3
Can you explain what you mean in the first bullet point? What "gotchas" ?
– M.M
Jun 3 at 8:08
2
And withstd::string_view
, you can define readable portions of the buffer.
– Zereges
Jun 3 at 8:09
16
@M.M For example the terminatingcharacter which
std::string
guarantees to be present. Would you know off-hand whether it is included in the valid range ofdata()
? Overwriting it is UB! It needs a few extra mental cycles (and possibly reference lookups) to verify this function does not have UB from that.
– Max Langhof
Jun 3 at 8:13
3
@M.M: The point is - you don't intuitively know! I wasn't even aware of what MaxLanghof wrote in his comment.
– einpoklum
Jun 3 at 9:56
5
+1 forstd::byte
, unbelievable I haven't heard of it before! It's crazy how often one looks into some C++ reference every day and still new things keep popping up...
– andreee
Jun 3 at 20:08
|
show 9 more comments
3
Can you explain what you mean in the first bullet point? What "gotchas" ?
– M.M
Jun 3 at 8:08
2
And withstd::string_view
, you can define readable portions of the buffer.
– Zereges
Jun 3 at 8:09
16
@M.M For example the terminatingcharacter which
std::string
guarantees to be present. Would you know off-hand whether it is included in the valid range ofdata()
? Overwriting it is UB! It needs a few extra mental cycles (and possibly reference lookups) to verify this function does not have UB from that.
– Max Langhof
Jun 3 at 8:13
3
@M.M: The point is - you don't intuitively know! I wasn't even aware of what MaxLanghof wrote in his comment.
– einpoklum
Jun 3 at 9:56
5
+1 forstd::byte
, unbelievable I haven't heard of it before! It's crazy how often one looks into some C++ reference every day and still new things keep popping up...
– andreee
Jun 3 at 20:08
3
3
Can you explain what you mean in the first bullet point? What "gotchas" ?
– M.M
Jun 3 at 8:08
Can you explain what you mean in the first bullet point? What "gotchas" ?
– M.M
Jun 3 at 8:08
2
2
And with
std::string_view
, you can define readable portions of the buffer.– Zereges
Jun 3 at 8:09
And with
std::string_view
, you can define readable portions of the buffer.– Zereges
Jun 3 at 8:09
16
16
@M.M For example the terminating
character which std::string
guarantees to be present. Would you know off-hand whether it is included in the valid range of data()
? Overwriting it is UB! It needs a few extra mental cycles (and possibly reference lookups) to verify this function does not have UB from that.– Max Langhof
Jun 3 at 8:13
@M.M For example the terminating
character which std::string
guarantees to be present. Would you know off-hand whether it is included in the valid range of data()
? Overwriting it is UB! It needs a few extra mental cycles (and possibly reference lookups) to verify this function does not have UB from that.– Max Langhof
Jun 3 at 8:13
3
3
@M.M: The point is - you don't intuitively know! I wasn't even aware of what MaxLanghof wrote in his comment.
– einpoklum
Jun 3 at 9:56
@M.M: The point is - you don't intuitively know! I wasn't even aware of what MaxLanghof wrote in his comment.
– einpoklum
Jun 3 at 9:56
5
5
+1 for
std::byte
, unbelievable I haven't heard of it before! It's crazy how often one looks into some C++ reference every day and still new things keep popping up...– andreee
Jun 3 at 20:08
+1 for
std::byte
, unbelievable I haven't heard of it before! It's crazy how often one looks into some C++ reference every day and still new things keep popping up...– andreee
Jun 3 at 20:08
|
show 9 more comments
You can completely avoid a manual memcpy
by calling the appropriate constructor:
std::string receive_data(const Receiver& receiver)
return receiver.data(), receiver.size();
That even handles in a string.
BTW, unless content is actually text, I would prefer std::vector<std::byte>
(or equivalent).
4
As original buffer data should not be notconst
, you should not have UB.
– Jarod42
Jun 3 at 7:56
14
And by then a good programmer goes: "Why the hell do I need a separate one-liner conversion function? Not only that, but this function named 'receive' doesn't actually perform any 'receiving'. Delete!"
– screwnut
Jun 3 at 8:32
6
@screwnut: To be fair, it is theoretically possible thatreceiver.data()
waits on the reception to happen, or does something else beyond returning a member pointer.
– einpoklum
Jun 3 at 22:53
9
@screwnut: Then I'm a bad programmer; I'll keep the conversion in a separate function, even if it's a one liner, because I appreciate abstraction and do not like to repeat myself. If I later need to add some checks, some logging, etc... the function is right here and I don't have to hunt around the codebase for all instances of conversion.
– Matthieu M.
Jun 4 at 6:58
5
@screwnut: Prefer non-member non-friend functions.
– Matthieu M.
Jun 4 at 10:57
|
show 5 more comments
You can completely avoid a manual memcpy
by calling the appropriate constructor:
std::string receive_data(const Receiver& receiver)
return receiver.data(), receiver.size();
That even handles in a string.
BTW, unless content is actually text, I would prefer std::vector<std::byte>
(or equivalent).
4
As original buffer data should not be notconst
, you should not have UB.
– Jarod42
Jun 3 at 7:56
14
And by then a good programmer goes: "Why the hell do I need a separate one-liner conversion function? Not only that, but this function named 'receive' doesn't actually perform any 'receiving'. Delete!"
– screwnut
Jun 3 at 8:32
6
@screwnut: To be fair, it is theoretically possible thatreceiver.data()
waits on the reception to happen, or does something else beyond returning a member pointer.
– einpoklum
Jun 3 at 22:53
9
@screwnut: Then I'm a bad programmer; I'll keep the conversion in a separate function, even if it's a one liner, because I appreciate abstraction and do not like to repeat myself. If I later need to add some checks, some logging, etc... the function is right here and I don't have to hunt around the codebase for all instances of conversion.
– Matthieu M.
Jun 4 at 6:58
5
@screwnut: Prefer non-member non-friend functions.
– Matthieu M.
Jun 4 at 10:57
|
show 5 more comments
You can completely avoid a manual memcpy
by calling the appropriate constructor:
std::string receive_data(const Receiver& receiver)
return receiver.data(), receiver.size();
That even handles in a string.
BTW, unless content is actually text, I would prefer std::vector<std::byte>
(or equivalent).
You can completely avoid a manual memcpy
by calling the appropriate constructor:
std::string receive_data(const Receiver& receiver)
return receiver.data(), receiver.size();
That even handles in a string.
BTW, unless content is actually text, I would prefer std::vector<std::byte>
(or equivalent).
edited Jun 6 at 12:16
Peter Mortensen
14.5k19 gold badges89 silver badges118 bronze badges
14.5k19 gold badges89 silver badges118 bronze badges
answered Jun 3 at 7:46
Jarod42Jarod42
133k12 gold badges117 silver badges210 bronze badges
133k12 gold badges117 silver badges210 bronze badges
4
As original buffer data should not be notconst
, you should not have UB.
– Jarod42
Jun 3 at 7:56
14
And by then a good programmer goes: "Why the hell do I need a separate one-liner conversion function? Not only that, but this function named 'receive' doesn't actually perform any 'receiving'. Delete!"
– screwnut
Jun 3 at 8:32
6
@screwnut: To be fair, it is theoretically possible thatreceiver.data()
waits on the reception to happen, or does something else beyond returning a member pointer.
– einpoklum
Jun 3 at 22:53
9
@screwnut: Then I'm a bad programmer; I'll keep the conversion in a separate function, even if it's a one liner, because I appreciate abstraction and do not like to repeat myself. If I later need to add some checks, some logging, etc... the function is right here and I don't have to hunt around the codebase for all instances of conversion.
– Matthieu M.
Jun 4 at 6:58
5
@screwnut: Prefer non-member non-friend functions.
– Matthieu M.
Jun 4 at 10:57
|
show 5 more comments
4
As original buffer data should not be notconst
, you should not have UB.
– Jarod42
Jun 3 at 7:56
14
And by then a good programmer goes: "Why the hell do I need a separate one-liner conversion function? Not only that, but this function named 'receive' doesn't actually perform any 'receiving'. Delete!"
– screwnut
Jun 3 at 8:32
6
@screwnut: To be fair, it is theoretically possible thatreceiver.data()
waits on the reception to happen, or does something else beyond returning a member pointer.
– einpoklum
Jun 3 at 22:53
9
@screwnut: Then I'm a bad programmer; I'll keep the conversion in a separate function, even if it's a one liner, because I appreciate abstraction and do not like to repeat myself. If I later need to add some checks, some logging, etc... the function is right here and I don't have to hunt around the codebase for all instances of conversion.
– Matthieu M.
Jun 4 at 6:58
5
@screwnut: Prefer non-member non-friend functions.
– Matthieu M.
Jun 4 at 10:57
4
4
As original buffer data should not be not
const
, you should not have UB.– Jarod42
Jun 3 at 7:56
As original buffer data should not be not
const
, you should not have UB.– Jarod42
Jun 3 at 7:56
14
14
And by then a good programmer goes: "Why the hell do I need a separate one-liner conversion function? Not only that, but this function named 'receive' doesn't actually perform any 'receiving'. Delete!"
– screwnut
Jun 3 at 8:32
And by then a good programmer goes: "Why the hell do I need a separate one-liner conversion function? Not only that, but this function named 'receive' doesn't actually perform any 'receiving'. Delete!"
– screwnut
Jun 3 at 8:32
6
6
@screwnut: To be fair, it is theoretically possible that
receiver.data()
waits on the reception to happen, or does something else beyond returning a member pointer.– einpoklum
Jun 3 at 22:53
@screwnut: To be fair, it is theoretically possible that
receiver.data()
waits on the reception to happen, or does something else beyond returning a member pointer.– einpoklum
Jun 3 at 22:53
9
9
@screwnut: Then I'm a bad programmer; I'll keep the conversion in a separate function, even if it's a one liner, because I appreciate abstraction and do not like to repeat myself. If I later need to add some checks, some logging, etc... the function is right here and I don't have to hunt around the codebase for all instances of conversion.
– Matthieu M.
Jun 4 at 6:58
@screwnut: Then I'm a bad programmer; I'll keep the conversion in a separate function, even if it's a one liner, because I appreciate abstraction and do not like to repeat myself. If I later need to add some checks, some logging, etc... the function is right here and I don't have to hunt around the codebase for all instances of conversion.
– Matthieu M.
Jun 4 at 6:58
5
5
@screwnut: Prefer non-member non-friend functions.
– Matthieu M.
Jun 4 at 10:57
@screwnut: Prefer non-member non-friend functions.
– Matthieu M.
Jun 4 at 10:57
|
show 5 more comments
Memcpy-ing to a const char pointer? AFAIK this does no harm as long as we know what we do, but is this good behavior and why?
The current code may have undefined behavior, depending on the C++ version. To avoid undefined behavior in C++14 and below take the address of the first element. It yields a non-const pointer:
buff.resize(size);
memcpy(&buff[0], &receiver[0], size);
I have recently seen a colleague of mine using
std::string
as a buffer...
That was somewhat common in older code, especially circa C++03. There are several benefits and downsides to using a string like that. Depending on what you are doing with the code, std::vector
can be a bit anemic, and you sometimes used a string instead and accepted the extra overhead of char_traits
.
For example, std::string
is usually a faster container than std::vector
on append, and you can't return std::vector
from a function. (Or you could not do so in practice in C++98 because C++98 required the vector to be constructed in the function and copied out). Additionally, std::string
allowed you to search with a richer assortment of member functions, like find_first_of
and find_first_not_of
. That was convenient when searching though arrays of bytes.
I think what you really want/need is SGI's Rope class, but it never made it into the STL. It looks like GCC's libstdc++ may provide it.
There a lengthy discussion about this being legal in C++14 and below:
const char* dst_ptr = buff.data();
const char* src_ptr = receiver.data();
memcpy((char*) dst_ptr, src_ptr, size);
I know for certain it is not safe in GCC. I once did something like this in some self tests and it resulted in a segfault:
std::string buff("A");
...
char* ptr = (char*)buff.data();
size_t len = buff.size();
ptr[0] ^= 1; // tamper with byte
bool tampered = HMAC(key, ptr, len, mac);
GCC put the single byte 'A'
in register AL
. The high 3-bytes were garbage, so the 32-bit register was 0xXXXXXX41
. When I dereferenced at ptr[0]
, GCC dereferenced a garbage address 0xXXXXXX41
.
The two take-aways for me were, don't write half-ass self tests, and don't try to make data()
a non-const pointer.
7
Preferstd::copy
for type-safety. It won't be slower.
– Lightness Races in Orbit
Jun 3 at 17:07
3
Seems like the only answer directly answering the question.
– Keith
Jun 4 at 0:59
2
"you can't returnstd::vector
from a function. (Or you could not do so back in C++98 or C++03)" is wrong.
– Ben Voigt
Jun 4 at 2:38
3
And it has never been a legal optimization for a compiler to confuse an address with stored content.buff.data()
cannot be a register containing'A'
, it must be an address.
– Ben Voigt
Jun 4 at 2:40
2
@jww: Indeed, although you could avoid that copy with NRVO and aswap()
call. But the copy would also be required ofstd::string
. Small string optimization could make it a little better. I think some implementations tried to solve this using copy-on-write (forstring
, never allowed forvector
) but even in C++98 and C++03 there were some specifications onstd::string
that couldn't be reasonable met by COW. Of course rvalue references and moves solved it neatly.
– Ben Voigt
Jun 4 at 3:01
|
show 3 more comments
Memcpy-ing to a const char pointer? AFAIK this does no harm as long as we know what we do, but is this good behavior and why?
The current code may have undefined behavior, depending on the C++ version. To avoid undefined behavior in C++14 and below take the address of the first element. It yields a non-const pointer:
buff.resize(size);
memcpy(&buff[0], &receiver[0], size);
I have recently seen a colleague of mine using
std::string
as a buffer...
That was somewhat common in older code, especially circa C++03. There are several benefits and downsides to using a string like that. Depending on what you are doing with the code, std::vector
can be a bit anemic, and you sometimes used a string instead and accepted the extra overhead of char_traits
.
For example, std::string
is usually a faster container than std::vector
on append, and you can't return std::vector
from a function. (Or you could not do so in practice in C++98 because C++98 required the vector to be constructed in the function and copied out). Additionally, std::string
allowed you to search with a richer assortment of member functions, like find_first_of
and find_first_not_of
. That was convenient when searching though arrays of bytes.
I think what you really want/need is SGI's Rope class, but it never made it into the STL. It looks like GCC's libstdc++ may provide it.
There a lengthy discussion about this being legal in C++14 and below:
const char* dst_ptr = buff.data();
const char* src_ptr = receiver.data();
memcpy((char*) dst_ptr, src_ptr, size);
I know for certain it is not safe in GCC. I once did something like this in some self tests and it resulted in a segfault:
std::string buff("A");
...
char* ptr = (char*)buff.data();
size_t len = buff.size();
ptr[0] ^= 1; // tamper with byte
bool tampered = HMAC(key, ptr, len, mac);
GCC put the single byte 'A'
in register AL
. The high 3-bytes were garbage, so the 32-bit register was 0xXXXXXX41
. When I dereferenced at ptr[0]
, GCC dereferenced a garbage address 0xXXXXXX41
.
The two take-aways for me were, don't write half-ass self tests, and don't try to make data()
a non-const pointer.
7
Preferstd::copy
for type-safety. It won't be slower.
– Lightness Races in Orbit
Jun 3 at 17:07
3
Seems like the only answer directly answering the question.
– Keith
Jun 4 at 0:59
2
"you can't returnstd::vector
from a function. (Or you could not do so back in C++98 or C++03)" is wrong.
– Ben Voigt
Jun 4 at 2:38
3
And it has never been a legal optimization for a compiler to confuse an address with stored content.buff.data()
cannot be a register containing'A'
, it must be an address.
– Ben Voigt
Jun 4 at 2:40
2
@jww: Indeed, although you could avoid that copy with NRVO and aswap()
call. But the copy would also be required ofstd::string
. Small string optimization could make it a little better. I think some implementations tried to solve this using copy-on-write (forstring
, never allowed forvector
) but even in C++98 and C++03 there were some specifications onstd::string
that couldn't be reasonable met by COW. Of course rvalue references and moves solved it neatly.
– Ben Voigt
Jun 4 at 3:01
|
show 3 more comments
Memcpy-ing to a const char pointer? AFAIK this does no harm as long as we know what we do, but is this good behavior and why?
The current code may have undefined behavior, depending on the C++ version. To avoid undefined behavior in C++14 and below take the address of the first element. It yields a non-const pointer:
buff.resize(size);
memcpy(&buff[0], &receiver[0], size);
I have recently seen a colleague of mine using
std::string
as a buffer...
That was somewhat common in older code, especially circa C++03. There are several benefits and downsides to using a string like that. Depending on what you are doing with the code, std::vector
can be a bit anemic, and you sometimes used a string instead and accepted the extra overhead of char_traits
.
For example, std::string
is usually a faster container than std::vector
on append, and you can't return std::vector
from a function. (Or you could not do so in practice in C++98 because C++98 required the vector to be constructed in the function and copied out). Additionally, std::string
allowed you to search with a richer assortment of member functions, like find_first_of
and find_first_not_of
. That was convenient when searching though arrays of bytes.
I think what you really want/need is SGI's Rope class, but it never made it into the STL. It looks like GCC's libstdc++ may provide it.
There a lengthy discussion about this being legal in C++14 and below:
const char* dst_ptr = buff.data();
const char* src_ptr = receiver.data();
memcpy((char*) dst_ptr, src_ptr, size);
I know for certain it is not safe in GCC. I once did something like this in some self tests and it resulted in a segfault:
std::string buff("A");
...
char* ptr = (char*)buff.data();
size_t len = buff.size();
ptr[0] ^= 1; // tamper with byte
bool tampered = HMAC(key, ptr, len, mac);
GCC put the single byte 'A'
in register AL
. The high 3-bytes were garbage, so the 32-bit register was 0xXXXXXX41
. When I dereferenced at ptr[0]
, GCC dereferenced a garbage address 0xXXXXXX41
.
The two take-aways for me were, don't write half-ass self tests, and don't try to make data()
a non-const pointer.
Memcpy-ing to a const char pointer? AFAIK this does no harm as long as we know what we do, but is this good behavior and why?
The current code may have undefined behavior, depending on the C++ version. To avoid undefined behavior in C++14 and below take the address of the first element. It yields a non-const pointer:
buff.resize(size);
memcpy(&buff[0], &receiver[0], size);
I have recently seen a colleague of mine using
std::string
as a buffer...
That was somewhat common in older code, especially circa C++03. There are several benefits and downsides to using a string like that. Depending on what you are doing with the code, std::vector
can be a bit anemic, and you sometimes used a string instead and accepted the extra overhead of char_traits
.
For example, std::string
is usually a faster container than std::vector
on append, and you can't return std::vector
from a function. (Or you could not do so in practice in C++98 because C++98 required the vector to be constructed in the function and copied out). Additionally, std::string
allowed you to search with a richer assortment of member functions, like find_first_of
and find_first_not_of
. That was convenient when searching though arrays of bytes.
I think what you really want/need is SGI's Rope class, but it never made it into the STL. It looks like GCC's libstdc++ may provide it.
There a lengthy discussion about this being legal in C++14 and below:
const char* dst_ptr = buff.data();
const char* src_ptr = receiver.data();
memcpy((char*) dst_ptr, src_ptr, size);
I know for certain it is not safe in GCC. I once did something like this in some self tests and it resulted in a segfault:
std::string buff("A");
...
char* ptr = (char*)buff.data();
size_t len = buff.size();
ptr[0] ^= 1; // tamper with byte
bool tampered = HMAC(key, ptr, len, mac);
GCC put the single byte 'A'
in register AL
. The high 3-bytes were garbage, so the 32-bit register was 0xXXXXXX41
. When I dereferenced at ptr[0]
, GCC dereferenced a garbage address 0xXXXXXX41
.
The two take-aways for me were, don't write half-ass self tests, and don't try to make data()
a non-const pointer.
edited Jun 4 at 7:57
Toby Speight
18.9k13 gold badges49 silver badges73 bronze badges
18.9k13 gold badges49 silver badges73 bronze badges
answered Jun 3 at 17:04
jwwjww
58.6k46 gold badges262 silver badges571 bronze badges
58.6k46 gold badges262 silver badges571 bronze badges
7
Preferstd::copy
for type-safety. It won't be slower.
– Lightness Races in Orbit
Jun 3 at 17:07
3
Seems like the only answer directly answering the question.
– Keith
Jun 4 at 0:59
2
"you can't returnstd::vector
from a function. (Or you could not do so back in C++98 or C++03)" is wrong.
– Ben Voigt
Jun 4 at 2:38
3
And it has never been a legal optimization for a compiler to confuse an address with stored content.buff.data()
cannot be a register containing'A'
, it must be an address.
– Ben Voigt
Jun 4 at 2:40
2
@jww: Indeed, although you could avoid that copy with NRVO and aswap()
call. But the copy would also be required ofstd::string
. Small string optimization could make it a little better. I think some implementations tried to solve this using copy-on-write (forstring
, never allowed forvector
) but even in C++98 and C++03 there were some specifications onstd::string
that couldn't be reasonable met by COW. Of course rvalue references and moves solved it neatly.
– Ben Voigt
Jun 4 at 3:01
|
show 3 more comments
7
Preferstd::copy
for type-safety. It won't be slower.
– Lightness Races in Orbit
Jun 3 at 17:07
3
Seems like the only answer directly answering the question.
– Keith
Jun 4 at 0:59
2
"you can't returnstd::vector
from a function. (Or you could not do so back in C++98 or C++03)" is wrong.
– Ben Voigt
Jun 4 at 2:38
3
And it has never been a legal optimization for a compiler to confuse an address with stored content.buff.data()
cannot be a register containing'A'
, it must be an address.
– Ben Voigt
Jun 4 at 2:40
2
@jww: Indeed, although you could avoid that copy with NRVO and aswap()
call. But the copy would also be required ofstd::string
. Small string optimization could make it a little better. I think some implementations tried to solve this using copy-on-write (forstring
, never allowed forvector
) but even in C++98 and C++03 there were some specifications onstd::string
that couldn't be reasonable met by COW. Of course rvalue references and moves solved it neatly.
– Ben Voigt
Jun 4 at 3:01
7
7
Prefer
std::copy
for type-safety. It won't be slower.– Lightness Races in Orbit
Jun 3 at 17:07
Prefer
std::copy
for type-safety. It won't be slower.– Lightness Races in Orbit
Jun 3 at 17:07
3
3
Seems like the only answer directly answering the question.
– Keith
Jun 4 at 0:59
Seems like the only answer directly answering the question.
– Keith
Jun 4 at 0:59
2
2
"you can't return
std::vector
from a function. (Or you could not do so back in C++98 or C++03)" is wrong.– Ben Voigt
Jun 4 at 2:38
"you can't return
std::vector
from a function. (Or you could not do so back in C++98 or C++03)" is wrong.– Ben Voigt
Jun 4 at 2:38
3
3
And it has never been a legal optimization for a compiler to confuse an address with stored content.
buff.data()
cannot be a register containing 'A'
, it must be an address.– Ben Voigt
Jun 4 at 2:40
And it has never been a legal optimization for a compiler to confuse an address with stored content.
buff.data()
cannot be a register containing 'A'
, it must be an address.– Ben Voigt
Jun 4 at 2:40
2
2
@jww: Indeed, although you could avoid that copy with NRVO and a
swap()
call. But the copy would also be required of std::string
. Small string optimization could make it a little better. I think some implementations tried to solve this using copy-on-write (for string
, never allowed for vector
) but even in C++98 and C++03 there were some specifications on std::string
that couldn't be reasonable met by COW. Of course rvalue references and moves solved it neatly.– Ben Voigt
Jun 4 at 3:01
@jww: Indeed, although you could avoid that copy with NRVO and a
swap()
call. But the copy would also be required of std::string
. Small string optimization could make it a little better. I think some implementations tried to solve this using copy-on-write (for string
, never allowed for vector
) but even in C++98 and C++03 there were some specifications on std::string
that couldn't be reasonable met by COW. Of course rvalue references and moves solved it neatly.– Ben Voigt
Jun 4 at 3:01
|
show 3 more comments
From C++17, data
can return a non const char *
.
Draft n4659 declares at [string.accessors]:
const charT* c_str() const noexcept;
const charT* data() const noexcept;
....
charT* data() noexcept;
8
@SergeBallesta - Removing a const qualifier is not UB. Modifying a const object is UB. The object in question is not const.
– StoryTeller
Jun 3 at 7:59
6
@SergeBallesta - Seriously? And how do you reconcile that with&str[0]
being a non-const pointer to the very same buffer? The object is guaranteed not to be const. The core rules of the language still apply, even to pointers returned from library types, ergo, no UB.
– StoryTeller
Jun 3 at 8:05
4
@Jarod42: I agree that I am nitpicking here, but the library could expect the buffer not be be changed, and later reuse a cached version. Coming for old K&R C I am now frightened by optimizing compilers and very cautious for constness and strict aliasing.
– Serge Ballesta
Jun 3 at 8:07
8
@StoryTeller Serge is correct that "Modifying the character array accessed through the const overload of data has undefined behavior." according to cppreference and the standard.
– Max Langhof
Jun 3 at 8:16
4
(For completeness, here is the C++11 wording: timsong-cpp.github.io/cppwp/n3337/string.ops#string.accessors-3)
– Max Langhof
Jun 3 at 8:22
|
show 13 more comments
From C++17, data
can return a non const char *
.
Draft n4659 declares at [string.accessors]:
const charT* c_str() const noexcept;
const charT* data() const noexcept;
....
charT* data() noexcept;
8
@SergeBallesta - Removing a const qualifier is not UB. Modifying a const object is UB. The object in question is not const.
– StoryTeller
Jun 3 at 7:59
6
@SergeBallesta - Seriously? And how do you reconcile that with&str[0]
being a non-const pointer to the very same buffer? The object is guaranteed not to be const. The core rules of the language still apply, even to pointers returned from library types, ergo, no UB.
– StoryTeller
Jun 3 at 8:05
4
@Jarod42: I agree that I am nitpicking here, but the library could expect the buffer not be be changed, and later reuse a cached version. Coming for old K&R C I am now frightened by optimizing compilers and very cautious for constness and strict aliasing.
– Serge Ballesta
Jun 3 at 8:07
8
@StoryTeller Serge is correct that "Modifying the character array accessed through the const overload of data has undefined behavior." according to cppreference and the standard.
– Max Langhof
Jun 3 at 8:16
4
(For completeness, here is the C++11 wording: timsong-cpp.github.io/cppwp/n3337/string.ops#string.accessors-3)
– Max Langhof
Jun 3 at 8:22
|
show 13 more comments
From C++17, data
can return a non const char *
.
Draft n4659 declares at [string.accessors]:
const charT* c_str() const noexcept;
const charT* data() const noexcept;
....
charT* data() noexcept;
From C++17, data
can return a non const char *
.
Draft n4659 declares at [string.accessors]:
const charT* c_str() const noexcept;
const charT* data() const noexcept;
....
charT* data() noexcept;
answered Jun 3 at 7:42
Serge BallestaSerge Ballesta
90.2k9 gold badges71 silver badges149 bronze badges
90.2k9 gold badges71 silver badges149 bronze badges
8
@SergeBallesta - Removing a const qualifier is not UB. Modifying a const object is UB. The object in question is not const.
– StoryTeller
Jun 3 at 7:59
6
@SergeBallesta - Seriously? And how do you reconcile that with&str[0]
being a non-const pointer to the very same buffer? The object is guaranteed not to be const. The core rules of the language still apply, even to pointers returned from library types, ergo, no UB.
– StoryTeller
Jun 3 at 8:05
4
@Jarod42: I agree that I am nitpicking here, but the library could expect the buffer not be be changed, and later reuse a cached version. Coming for old K&R C I am now frightened by optimizing compilers and very cautious for constness and strict aliasing.
– Serge Ballesta
Jun 3 at 8:07
8
@StoryTeller Serge is correct that "Modifying the character array accessed through the const overload of data has undefined behavior." according to cppreference and the standard.
– Max Langhof
Jun 3 at 8:16
4
(For completeness, here is the C++11 wording: timsong-cpp.github.io/cppwp/n3337/string.ops#string.accessors-3)
– Max Langhof
Jun 3 at 8:22
|
show 13 more comments
8
@SergeBallesta - Removing a const qualifier is not UB. Modifying a const object is UB. The object in question is not const.
– StoryTeller
Jun 3 at 7:59
6
@SergeBallesta - Seriously? And how do you reconcile that with&str[0]
being a non-const pointer to the very same buffer? The object is guaranteed not to be const. The core rules of the language still apply, even to pointers returned from library types, ergo, no UB.
– StoryTeller
Jun 3 at 8:05
4
@Jarod42: I agree that I am nitpicking here, but the library could expect the buffer not be be changed, and later reuse a cached version. Coming for old K&R C I am now frightened by optimizing compilers and very cautious for constness and strict aliasing.
– Serge Ballesta
Jun 3 at 8:07
8
@StoryTeller Serge is correct that "Modifying the character array accessed through the const overload of data has undefined behavior." according to cppreference and the standard.
– Max Langhof
Jun 3 at 8:16
4
(For completeness, here is the C++11 wording: timsong-cpp.github.io/cppwp/n3337/string.ops#string.accessors-3)
– Max Langhof
Jun 3 at 8:22
8
8
@SergeBallesta - Removing a const qualifier is not UB. Modifying a const object is UB. The object in question is not const.
– StoryTeller
Jun 3 at 7:59
@SergeBallesta - Removing a const qualifier is not UB. Modifying a const object is UB. The object in question is not const.
– StoryTeller
Jun 3 at 7:59
6
6
@SergeBallesta - Seriously? And how do you reconcile that with
&str[0]
being a non-const pointer to the very same buffer? The object is guaranteed not to be const. The core rules of the language still apply, even to pointers returned from library types, ergo, no UB.– StoryTeller
Jun 3 at 8:05
@SergeBallesta - Seriously? And how do you reconcile that with
&str[0]
being a non-const pointer to the very same buffer? The object is guaranteed not to be const. The core rules of the language still apply, even to pointers returned from library types, ergo, no UB.– StoryTeller
Jun 3 at 8:05
4
4
@Jarod42: I agree that I am nitpicking here, but the library could expect the buffer not be be changed, and later reuse a cached version. Coming for old K&R C I am now frightened by optimizing compilers and very cautious for constness and strict aliasing.
– Serge Ballesta
Jun 3 at 8:07
@Jarod42: I agree that I am nitpicking here, but the library could expect the buffer not be be changed, and later reuse a cached version. Coming for old K&R C I am now frightened by optimizing compilers and very cautious for constness and strict aliasing.
– Serge Ballesta
Jun 3 at 8:07
8
8
@StoryTeller Serge is correct that "Modifying the character array accessed through the const overload of data has undefined behavior." according to cppreference and the standard.
– Max Langhof
Jun 3 at 8:16
@StoryTeller Serge is correct that "Modifying the character array accessed through the const overload of data has undefined behavior." according to cppreference and the standard.
– Max Langhof
Jun 3 at 8:16
4
4
(For completeness, here is the C++11 wording: timsong-cpp.github.io/cppwp/n3337/string.ops#string.accessors-3)
– Max Langhof
Jun 3 at 8:22
(For completeness, here is the C++11 wording: timsong-cpp.github.io/cppwp/n3337/string.ops#string.accessors-3)
– Max Langhof
Jun 3 at 8:22
|
show 13 more comments
The code is unnecessary, considering that
std::string receive_data(const Receiver& receiver)
std::string buff;
int size = receiver.size();
if (size > 0)
buff.assign(receiver.data(), size);
return buff;
will do exactly the same.
2
You can cut even more code; theif
is also unnecessary.assign
will be a no-op then. But continue to unnecessary remove code, and you end up with Jarod42's answer. None of these lines are necessary, asstd::string
already has an appropriate constructor.
– MSalters
Jun 4 at 11:32
@MSalters I prefer not to assume things that are not given. What ifreceiver.size()
can return negative values?
– Kit.
Jun 5 at 14:02
That would be rather unexpected, given that sizes are typically asize_t
and therefore unsigned. That does show a possible problem with your code: it might suffer from signed integer overflow, which is undefined behavior. And that's on a code path which handles input, so this may constitute an externally exploitable vulnerability.
– MSalters
Jun 6 at 8:53
@MSalters True, Jarod42's changes could introduce an externally exploitable vulnerability. They could also introduce crashes ifreceiver.data()
is UB whenreceiver.size()
is zero.
– Kit.
Jun 7 at 7:41
add a comment
|
The code is unnecessary, considering that
std::string receive_data(const Receiver& receiver)
std::string buff;
int size = receiver.size();
if (size > 0)
buff.assign(receiver.data(), size);
return buff;
will do exactly the same.
2
You can cut even more code; theif
is also unnecessary.assign
will be a no-op then. But continue to unnecessary remove code, and you end up with Jarod42's answer. None of these lines are necessary, asstd::string
already has an appropriate constructor.
– MSalters
Jun 4 at 11:32
@MSalters I prefer not to assume things that are not given. What ifreceiver.size()
can return negative values?
– Kit.
Jun 5 at 14:02
That would be rather unexpected, given that sizes are typically asize_t
and therefore unsigned. That does show a possible problem with your code: it might suffer from signed integer overflow, which is undefined behavior. And that's on a code path which handles input, so this may constitute an externally exploitable vulnerability.
– MSalters
Jun 6 at 8:53
@MSalters True, Jarod42's changes could introduce an externally exploitable vulnerability. They could also introduce crashes ifreceiver.data()
is UB whenreceiver.size()
is zero.
– Kit.
Jun 7 at 7:41
add a comment
|
The code is unnecessary, considering that
std::string receive_data(const Receiver& receiver)
std::string buff;
int size = receiver.size();
if (size > 0)
buff.assign(receiver.data(), size);
return buff;
will do exactly the same.
The code is unnecessary, considering that
std::string receive_data(const Receiver& receiver)
std::string buff;
int size = receiver.size();
if (size > 0)
buff.assign(receiver.data(), size);
return buff;
will do exactly the same.
answered Jun 3 at 7:49
Kit.Kit.
1,1997 silver badges9 bronze badges
1,1997 silver badges9 bronze badges
2
You can cut even more code; theif
is also unnecessary.assign
will be a no-op then. But continue to unnecessary remove code, and you end up with Jarod42's answer. None of these lines are necessary, asstd::string
already has an appropriate constructor.
– MSalters
Jun 4 at 11:32
@MSalters I prefer not to assume things that are not given. What ifreceiver.size()
can return negative values?
– Kit.
Jun 5 at 14:02
That would be rather unexpected, given that sizes are typically asize_t
and therefore unsigned. That does show a possible problem with your code: it might suffer from signed integer overflow, which is undefined behavior. And that's on a code path which handles input, so this may constitute an externally exploitable vulnerability.
– MSalters
Jun 6 at 8:53
@MSalters True, Jarod42's changes could introduce an externally exploitable vulnerability. They could also introduce crashes ifreceiver.data()
is UB whenreceiver.size()
is zero.
– Kit.
Jun 7 at 7:41
add a comment
|
2
You can cut even more code; theif
is also unnecessary.assign
will be a no-op then. But continue to unnecessary remove code, and you end up with Jarod42's answer. None of these lines are necessary, asstd::string
already has an appropriate constructor.
– MSalters
Jun 4 at 11:32
@MSalters I prefer not to assume things that are not given. What ifreceiver.size()
can return negative values?
– Kit.
Jun 5 at 14:02
That would be rather unexpected, given that sizes are typically asize_t
and therefore unsigned. That does show a possible problem with your code: it might suffer from signed integer overflow, which is undefined behavior. And that's on a code path which handles input, so this may constitute an externally exploitable vulnerability.
– MSalters
Jun 6 at 8:53
@MSalters True, Jarod42's changes could introduce an externally exploitable vulnerability. They could also introduce crashes ifreceiver.data()
is UB whenreceiver.size()
is zero.
– Kit.
Jun 7 at 7:41
2
2
You can cut even more code; the
if
is also unnecessary. assign
will be a no-op then. But continue to unnecessary remove code, and you end up with Jarod42's answer. None of these lines are necessary, as std::string
already has an appropriate constructor.– MSalters
Jun 4 at 11:32
You can cut even more code; the
if
is also unnecessary. assign
will be a no-op then. But continue to unnecessary remove code, and you end up with Jarod42's answer. None of these lines are necessary, as std::string
already has an appropriate constructor.– MSalters
Jun 4 at 11:32
@MSalters I prefer not to assume things that are not given. What if
receiver.size()
can return negative values?– Kit.
Jun 5 at 14:02
@MSalters I prefer not to assume things that are not given. What if
receiver.size()
can return negative values?– Kit.
Jun 5 at 14:02
That would be rather unexpected, given that sizes are typically a
size_t
and therefore unsigned. That does show a possible problem with your code: it might suffer from signed integer overflow, which is undefined behavior. And that's on a code path which handles input, so this may constitute an externally exploitable vulnerability.– MSalters
Jun 6 at 8:53
That would be rather unexpected, given that sizes are typically a
size_t
and therefore unsigned. That does show a possible problem with your code: it might suffer from signed integer overflow, which is undefined behavior. And that's on a code path which handles input, so this may constitute an externally exploitable vulnerability.– MSalters
Jun 6 at 8:53
@MSalters True, Jarod42's changes could introduce an externally exploitable vulnerability. They could also introduce crashes if
receiver.data()
is UB when receiver.size()
is zero.– Kit.
Jun 7 at 7:41
@MSalters True, Jarod42's changes could introduce an externally exploitable vulnerability. They could also introduce crashes if
receiver.data()
is UB when receiver.size()
is zero.– Kit.
Jun 7 at 7:41
add a comment
|
The big optimization opportunity I would investigate here is: Receiver
appears to be some kind of container that supports .data()
and .size()
. If you can consume it, and pass it in as a rvalue reference Receiver&&
, you might be able to use move semantics without making any copies at all! If it’s got an iterator interface, you could use those for range-based constructors or std::move()
from <algorithm>
.
In C++17 (as Serge Ballesta and others have mentioned), std::string::data()
returns a pointer to non-const data. A std::string
has been guaranteed to store all its data contiguously for years.
The code as written smells a bit, although it’s not really the programmer’s fault: those hacks were necessary at the time. Today, you should at least change the type of dst_ptr
from const char*
to char*
and remove the cast in the first argument to memcpy()
. You could also reserve()
a number of bytes for the buffer and then use a STL function to move the data.
As others have mentioned, a std::vector
or std::unique_ptr
would be a more natural data structure to use here.
add a comment
|
The big optimization opportunity I would investigate here is: Receiver
appears to be some kind of container that supports .data()
and .size()
. If you can consume it, and pass it in as a rvalue reference Receiver&&
, you might be able to use move semantics without making any copies at all! If it’s got an iterator interface, you could use those for range-based constructors or std::move()
from <algorithm>
.
In C++17 (as Serge Ballesta and others have mentioned), std::string::data()
returns a pointer to non-const data. A std::string
has been guaranteed to store all its data contiguously for years.
The code as written smells a bit, although it’s not really the programmer’s fault: those hacks were necessary at the time. Today, you should at least change the type of dst_ptr
from const char*
to char*
and remove the cast in the first argument to memcpy()
. You could also reserve()
a number of bytes for the buffer and then use a STL function to move the data.
As others have mentioned, a std::vector
or std::unique_ptr
would be a more natural data structure to use here.
add a comment
|
The big optimization opportunity I would investigate here is: Receiver
appears to be some kind of container that supports .data()
and .size()
. If you can consume it, and pass it in as a rvalue reference Receiver&&
, you might be able to use move semantics without making any copies at all! If it’s got an iterator interface, you could use those for range-based constructors or std::move()
from <algorithm>
.
In C++17 (as Serge Ballesta and others have mentioned), std::string::data()
returns a pointer to non-const data. A std::string
has been guaranteed to store all its data contiguously for years.
The code as written smells a bit, although it’s not really the programmer’s fault: those hacks were necessary at the time. Today, you should at least change the type of dst_ptr
from const char*
to char*
and remove the cast in the first argument to memcpy()
. You could also reserve()
a number of bytes for the buffer and then use a STL function to move the data.
As others have mentioned, a std::vector
or std::unique_ptr
would be a more natural data structure to use here.
The big optimization opportunity I would investigate here is: Receiver
appears to be some kind of container that supports .data()
and .size()
. If you can consume it, and pass it in as a rvalue reference Receiver&&
, you might be able to use move semantics without making any copies at all! If it’s got an iterator interface, you could use those for range-based constructors or std::move()
from <algorithm>
.
In C++17 (as Serge Ballesta and others have mentioned), std::string::data()
returns a pointer to non-const data. A std::string
has been guaranteed to store all its data contiguously for years.
The code as written smells a bit, although it’s not really the programmer’s fault: those hacks were necessary at the time. Today, you should at least change the type of dst_ptr
from const char*
to char*
and remove the cast in the first argument to memcpy()
. You could also reserve()
a number of bytes for the buffer and then use a STL function to move the data.
As others have mentioned, a std::vector
or std::unique_ptr
would be a more natural data structure to use here.
edited Jun 4 at 17:53
answered Jun 3 at 21:29
DavislorDavislor
10.4k2 gold badges18 silver badges30 bronze badges
10.4k2 gold badges18 silver badges30 bronze badges
add a comment
|
add a comment
|
One downside is performance.
The .resize method will default-initialize all the new byte locations to 0.
That initialization is unnecessary if you're then going to overwrite the 0s with other data.
add a comment
|
One downside is performance.
The .resize method will default-initialize all the new byte locations to 0.
That initialization is unnecessary if you're then going to overwrite the 0s with other data.
add a comment
|
One downside is performance.
The .resize method will default-initialize all the new byte locations to 0.
That initialization is unnecessary if you're then going to overwrite the 0s with other data.
One downside is performance.
The .resize method will default-initialize all the new byte locations to 0.
That initialization is unnecessary if you're then going to overwrite the 0s with other data.
answered Jun 5 at 21:40
WaxratWaxrat
1813 bronze badges
1813 bronze badges
add a comment
|
add a comment
|
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f56422913%2fare-there-downsides-to-using-stdstring-as-a-buffer%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
28
With C++17
data()
has an overload returning a pointer to non-const qualified char.– VTT
Jun 3 at 7:35
38
...which is mentioned by cppreference. cplusplus.com is not the best source.
– HolyBlackCat
Jun 3 at 7:36
11
I think that the operation of casting from
const char*
tochar*
by itself implies some sort of hazard in your program. Ifdst_ptr
points to a read-only memory block, then you should not attempt to write into that block using this pointer.– goodvibration
Jun 3 at 7:37
6
Whenever you see code using C-style casts (like e.g.
(char*) dst_ptr
) you should take that as a red flag.– Some programmer dude
Jun 3 at 7:38
9
I think this question is mostly opinion-based. IMO using
std::string
as a buffer is fine if you know you're receiving text data. If you're receiving binary datastd::vector<char>
is probably a better choice.– Miles Budnek
Jun 3 at 7:42