38

In the below code I am trying to replace the first character with '\0'.

I expected it to print an empty string, but in the output it just omits that and displays the rest of the characters.

int main()
{
    string str;
    cin >> str;

    str[0] = '\0';

    cout << str << "\n";
    return 0;
}

OUTPUT :

   testing
   esting

How do I terminate the string in C++?

PS: I was trying this approach of terminating the string in a different question in which I need to terminate in between.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user3351750
  • 927
  • 13
  • 24
  • NULL is a `#define` for a Null-pointer in the language `C`. It is sometimes used as pointer value in `C++` too, but this is not consensus. But NULL is never a character constant. – harper Jun 27 '14 at 13:41
  • 2
    If you try `str.c_str()` it might give the output you want, but I don't recommend using strings this way. – Neil Kirk Jun 27 '14 at 13:42
  • 1
    Be very careful when try to manipulate the internals of `std::string`. The memory is generally laid out in a contiguous fashion (IIRC because of the requirements of `std::string.c_str()` but you should not assume too much about how the container (`std::string`) manipulates and uses that memory. It is ill-advised to attempt to manipulate the raw contents without the container's knowledge. Use the `std::string()` methods where necessary, there are many, and the standard algorithms where required; they will work well together. – Niall Jun 27 '14 at 13:59
  • 3
    @Niall You can't manipulate the internals of `std::string` in general, but you can modify specific characters by indexing into the string, as he has done, or by dereferencing a valid iterator, and there are also a number of non-const member functions which permit explicit manipulations. – James Kanze Jun 27 '14 at 14:06
  • @JamesKanze Correct, the manipulation above is via the member `operator[]` – Niall Jun 27 '14 at 14:09
  • The `NUL` character is not omitted by `cout`, the `NUL` character is written to `stdout`. The terminal receives the `NUL` character and ignore it. – kasperd Jun 28 '14 at 05:56
  • 1
    @Niall: C++11 requires `std::string` to be contiguous. But even in C++03, it's totally acceptable to do what the OP is doing. You can modify any character in the string (i.e. `str[i] = anyChar` for any `0 ≤ i < str.size()`) without "breaking" the string at all. FWIW, `operator[]` *is* a method in `std::string` intentionally added so people can do things like this. – Cornstalks Jun 28 '14 at 18:42

7 Answers7

52

std::string is not a null terminated string. If you want to empty it use the clear() method. If you want to remove an element of the string use the erase() method.

Marius Bancila
  • 16,053
  • 9
  • 49
  • 91
  • 6
    @Niall It is, in fact, guaranteed not to be implemented as a null terminated character array, since it's perfectly legal for an `std::string` to contain a `'\0'`. – James Kanze Jun 27 '14 at 14:04
  • 1
    @JamesKanze Correct, did we not say the same thing? – Niall Jun 27 '14 at 14:07
  • 3
    @Niall your "is not guaranteed" statement allows for it to be possible that `std::string` is implemented as null-terminated, just not guaranteed as such. James' "is guaranteed not" statement allows only for non–null-terminated implementations, which is correct. – Jon Hanna Jun 27 '14 at 14:16
  • 2
    @Niall You said "is not guaranteed", I said that it was actually forbidden. – James Kanze Jun 27 '14 at 14:18
  • @JamesKanze Do you have reference for that in the specification, where is it forbidden to be implemented like that? I thought the implementation was free to layout the memory backing a string as it requires. I agree it may contain '\0' my comment was talking to the fact that you should make no assumptions about how it is laid out in memory. – Niall Jun 27 '14 at 14:24
  • 4
    @Niall The fact that you can put a `'\0'` in a string means that the implementation cannot use it as a sentinel. (C++11 does require that the memory be contiguous, and that `c_str()` return the same thing as `&str[0]`, but that still doesn't allow the implementation to treat `'\0'` as a terminator.) – James Kanze Jun 27 '14 at 14:35
  • @JamesKanze Correct. I agree, the `'\0'` mustn't be used as a sentinel. It if for these very reason that C++ `std::string` must not be thought of as a C-style character array. My apologies if we were talking past each other, or the confusion from my first comment. – Niall Jun 27 '14 at 14:44
  • @Niall: You seem to confuse "char array" and "null-terminated string". A null-terminated string is a char array of which only the last char is null \0. As `std::string` must be able to hold any char array, it can't be restricted to the subset of null-terminated strings. – MSalters Jun 27 '14 at 16:02
  • @JamesKanze Technically you could still use `\0` as a sentinel and only switch to a different internal representation when the user tries to insert a `\0` character. It doesn't really make much sense to do it, since it would only make the implementation more complex and less efficient due to the overhead of checking for `\0` in every operation, but it's still possible. – Bakuriu Jun 27 '14 at 18:31
35

There are two approaches to strings.

In C, strings are zero-terminated, meaning the '\0' indicates the end of the string, which is what you are expected.

C++ (and most languages) uses counted strings, meaning the end of the string is indexed, so adding null terminators to the string won't terminate it. '\0' is a non-printing character, so when you print, you get the behavior you see. If you want to manipulate std::string length, you need to use the std::string methods (http://www.cplusplus.com/reference/string/string/).

C++ doesn't care about null terminators for strings. They are just used for C compatibility.

Incidentally, this should have the behavior you were expecting.

cout<<str.c_str()<<"\n";

See also

Why null-terminated strings? Or: null-terminated vs. characters + length storage

What's the rationale for null terminated strings?

Community
  • 1
  • 1
QuestionC
  • 10,006
  • 4
  • 26
  • 44
  • +1, though "C++ uses length prefixed strings" - not literally "prefixed" (as in say Borland Pascal) - if there's a length data member at all it will almost certainly be a separate data member from the pointer-to-dynamically-allocated-characters, and not overlaid with the first few bytes thereof, though with short string optimisation it could coincidentally sometimes be a prefixed in terms of memory layout. Other string implementations could store a pointer to the end of the data rather than a size. – Tony Delroy Jun 27 '14 at 13:55
  • 1
    I was really hung up on how to describe C++'s style of string storage and fell back on what I had read about pascal once upon a time. What's a better term for the general practice of indexing the last character? – QuestionC Jun 27 '14 at 13:58
  • 1
    I always knew them as "counted strings". – Matteo Italia Jun 27 '14 at 22:46
  • 1
    Not sure how to say it concisely :-/. I'd tend to say something like "std::string has a size member, separate from the textual data, so it doesn't need a sentinel to mark the end." – Tony Delroy Jun 28 '14 at 05:27
14

You just think you got

testing
esting

as output, but you actually got

testing
 esting
^
|
+-- "empty" \0 char

because the std::string still has the length of "testing", you just replaced the first character 't' with '\0'. When std::cout gets the string, it looks at the string length and outputs all its characters, which makes the '\0' cause an "empty" slot in the output.

To really clear the string, prefer to call std::string::clear(). Also valid is std::string::reset(0), but it's not as expressive (you can even assign an empty string... shudder). The implementation may or may not use a '\0' at all, so don't think of that as a way to fiddle with the externally observed representation.

Johann Gerell
  • 24,991
  • 10
  • 72
  • 122
10

A std::string resembles pretty much the functionality of a std::vector. If you want to erase all the elements of your std::string you could use std::string::clear

#include <iostream>
#include <string>

int main() {
  std::string str("testing");
  std::cout << str << std::endl;
  str.clear();
  std::cout << str << std::endl;
}

If you want to delete a particular character from your string (e.g., the 1st charater) you could use std::string::erase:

#include <iostream>
#include <string>

int main() {
  std::string str("testing");
  std::cout << str << std::endl;
  str.erase(str.begin());
  std::cout << str << std::endl;
}

If you want to remove particular characters from your string you could, as in the case of std::vector, use the erase-remove idiom:

#include <iostream>
#include <string>
#include <algorithm>

int main() {
  std::string str("testing");
  std::cout << str << std::endl;
  str.erase(std::remove_if(str.begin(), str.end(), [](char const &c){ return c == 't'; }), str.end());
  std::cout << str << std::endl;
}
101010
  • 41,839
  • 11
  • 94
  • 168
4

According to the std::string implementation, the [] return the reference for the character in given position in the internal char array. When you set as str[8]= ‘\0’ it sets. You can check it by calling str.c_str() function. It return the internal array.

However, The cout reads it characters without null chars in the middle. It is the reason for the output.

const_reference operator[](size_type __n) const
{
    return *(_M_start + __n);
}

reference operator[](size_type __n)
{
    return *(_M_start + __n);
}
Sujith Gunawardhane
  • 1,251
  • 1
  • 10
  • 24
4

To get string that ends on first character you need to first extract char const* from string by calling method string::c_str(). Then it will be processed as you wish (like in C).

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Dakorn
  • 883
  • 6
  • 11
3

Maybe use the good old printf like printf("%s", str.c_str()); this will print it out C style, and should terminate on the null character. things like strlen(str.c_str()); should work as well. What I suspect is that since C++ strings have a .size() function somewhere it probably has a int size; member, they don't waste any time checking for a null character every time they want to print. They can probably just say to print str.size() characters.

Jacob Minshall
  • 1,044
  • 9
  • 15