9

I know string objects aren't null terminated but why should this work?

std::string S("Hey");
for(int i = 0; S[i] != '\0'; ++i)
   std::cout << S[i];

So the constructor copies the null terminator as well, but does not increment the length? Why does it bother?

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
nek28
  • 259
  • 1
  • 2
  • 6

3 Answers3

10

So the constructor copies the null terminator as well, but does not increment the length?

As you've known that std::string doesn't contain the null character (and it doesn't copy the null character here).

The point is that you're using std::basic_string::operator[]. According to C++11, std::basic_string::operator[] will return a null character when specified index is equivalent to size().

If pos == size(), a reference to the character with value CharT() (the null character) is returned.

For the first (non-const) version, the behavior is undefined if this character is modified to any value other than charT().

Community
  • 1
  • 1
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
10

std::string stores its data internally in the form of a null-terminated C-string, but in normal usage does not allow you to access the null terminator.

For example, if I assign the value "Hello, World!" to a string, the internal buffer will look like this:

std::string myString("Hello, World!");

// Internal Buffer...
// [ H | e | l | l | o | , |   | W | o | r | d | ! | \0 ]
//                                                   ^ Null terminator.

In this example, the null terminator was NOT copied from the end of the string literal, but added internally by std::string.

As @songyuanyao mentions in his answer, the result of this is that myString[myString.size()]; returns '\0'.

So why does std::string assign a null terminator to the end of the string? It certainly doesn't have to support one, because you can add '\0' to a string and it is included in the string:

std::string myString;
myString.size();              // 0
myString.push_back('\0');
myString.size();              // 1

The reason for this behavior is to support the std::string::c_str() function. The c_str() function is required to return a null-terminated const char *. The most efficient way to do this is to simply return a pointer to the internal buffer, but in order to do that the internal buffer must include a null terminator character at the end of the string. Since C++11, strings are required to include the null terminator to support this.

P.S. While not strictly part of your question, it should be pointed out that the loop from your question might NOT return a full string if your string includes null characters:

std::string S("Hey");
S.push_back('\0');
S.append("Jude");

for(int i = 0; S[i] != '\0'; ++i)
    std::cout << S[i];

// Only "Hey" is printed!
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
Karl Nicoll
  • 16,090
  • 3
  • 51
  • 65
0
  • std::string Overload: The std::string class has a special way to store its length, so it doesn't need null-termination like C-style strings. You can use S[i] to access characters directly, and the loop works without '\0'.

  • Efficient Handling: The std::string constructor doesn't add a null terminator when creating the string because it uses a smarter length management method. This approach is more efficient for handling and manipulating strings.

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Aug 08 '23 at 10:48