问题
Consider the following code:
const char foo[] = "lorem ipsum"; // foo is an array of 12 characters
const auto length = strlen(foo); // length is 11
string bar(length, '\0'); // bar was constructed with string(11, '\0')
strncpy(data(bar), foo, length);
cout << data(bar) << endl;
My understanding is that string
s are always allocated with a hidden null element. If this is the case then bar
really allocates 12 characters, with the 12th being a hidden '\0'
and this is perfectly safe... If I'm wrong on that then the cout
will result in undefined behavior because there isn't a null terminator.
Can someone confirm for me? Is this legal?
There have been a lot of questions about why to use strncpy
instead of just using the string(const char*, const size_t)
constructor. My intent has been to make my toy code close to my actual code which contains a vsnprintf
. Unfortunately even after getting excellent answers here I've found that vsnprintf
doesn't behave the same as strncpy
, and I've asked a follow up question here: Why is vsnprintf Not Writing the Same Number of Characters as strncpy Would?
回答1:
This is safe, as long as you copy [0, size())
characters into the string . Per [basic.string]/3
In all cases,
[data(), data() + size()]
is a valid range,data() + size()
points at an object with valuecharT()
(a “null terminator”), andsize() <= capacity()
istrue
.
So string bar(length, '\0')
gives you a string with a size()
of 11, with an immutable null terminator at the end (for a total of 12 characters in actual size). As long as you do not overwrite that null terminator, or try to write past it, you're okay.
回答2:
There are two different things here.
First, does strncpy
add an additional \0
in this instance (11 non-\0
elements to be copied in a string of size 11). The answer is no:
Copies at most count characters of the byte string pointed to by src (including the terminating null character) to character array pointed to by dest.
If count is reached before the entire string src was copied, the resulting character array is not null-terminated.
So the call is perfectly fine.
Then data()
gives you a proper \0
-terminated string:
c_str() and data() perform the same function. (since C++11)
So it seems that for C++11, you are safe. Whether the string allocates an additional \0
or not doesn't seems to be indicated in the documentation, but the API is clear that what you are doing is perfectly fine.
回答3:
You have allocated an 11-character std::string
. You are not trying to read nor write anything past that, so that part will be safe.
So the real question is whether you have messed up the internals of the string. Since you haven't done anything that isn't allowed, how would that be possible? If it's required for the string to internally keep a 12-byte buffer with a null padding at the end in order to fulfill its contract, that will be the case no matter what operations you performed.
回答4:
Yes it's safe according to the char * strncpy(char* destination, const char* source, size_t num):
Copy characters from string
Copies the first num characters of source to destination. If the end of the source C string (which is signaled by a null-character) is found before num characters have been copied, destination is padded with zeros until a total of num characters have been written to it.
来源:https://stackoverflow.com/questions/54132563/is-it-safe-to-strncpy-into-a-string-that-doesnt-have-room-for-the-null-terminat