4.12 — An introduction to std::string

The very first C++ program you wrote probably looked something like this:

So what is “Hello, world!” exactly? “Hello, world!” is a collection of sequential characters called a string. In C++, we use strings to represent text such as names, addresses, words, and sentences. String literals (such as “Hello, world!\n”) are placed between double quotes to identify them as strings.

Because strings are commonly used in programs, most modern programming languages include a fundamental string data type. In C++, strings aren’t a fundamental type (they’re actually a compound type, and defined in the C++ standard library rather than as part of the core language). But strings are straightforward and useful enough that we’ll introduce them here rather than wait until the chapter on compound types (chapter 8).


To use strings in C++, we first need to #include the <string> header to bring in the declarations for std::string. Once that is done, we can define variables of type std::string.

Just like normal variables, you can initialize or assign values to strings as you would expect:

Note that strings can hold numbers as well:

In string form, numbers are treated as text, not numbers, and thus they can not be manipulated as numbers (e.g. you can’t multiply them). C++ will not automatically convert string numbers to integer or floating point values.

String output

Strings can be output as expected using std::cout:

This prints:

My name is: Alex

Empty strings will print nothing:

Which prints:


String input with std::cin

Using strings with std::cin may yield some surprises! Consider the following example:

Here’s the results from a sample run of this program:

Enter your full name: John Doe
Enter your age: Your name is John and your age is Doe

Hmmm, that isn’t right! What happened? It turns out that when using operator>> to extract a string from cin, operator>> only returns characters up to the first whitespace it encounters. Any other characters are left inside std::cin, waiting for the next extraction.

So when we used operator>> to extract a string into variable name, only "John" was extracted, leaving " Doe" inside std::cin. When we then used operator>> to get variable age, it extracted "Doe" instead of waiting for us to input an age. Then the program ends.

Use std::getline() to input text

To read a full line of input into a string, you’re better off using the std::getline() function instead. std::getline() takes two parameters: the first is std::cin, and the second is your string variable.

Here’s the same program as above using std::getline():

Now our program works as expected:

Enter your full name: John Doe
Enter your age: 23
Your name is John Doe and your age is 23

What the heck is std::ws?

In lesson 4.8 -- Floating point numbers, we discussed output manipulators, which allow us to alter the way output is displayed. In that lesson, we used the output manipulator function std::setprecision() to change the number of digits of precision that std::cout displayed.

C++ also supports input manipulators (defined in the iomanip header), which alter the way that input is accepted. The std::ws input manipulator tells std::cin to ignore any leading whitespace. Note that std::ws is not a function.

Let’s explore why this is useful. Consider the following program:

Here’s some output from this program:

Pick 1 or 2: 2
Now enter your name: Hello, , you picked 2

This program first asks you to enter 1 or 2, and waits for you to do so. All good so far. Then it will ask you to enter your name. However, it won’t actually wait for you to enter your name! Instead, it prints the “Hello” string, and then exits. What happened?

It turns out, when you enter a value using operator>>, std::cin not only captures the value, it also captures the newline character ('\n') that occurs when you hit the enter key. So when we type 2 and then hit enter, std::cin receives gets the string "2\n". It then extracts the 2 to variable choice, leaving the newline character behind for later. Then, when std::getline() goes to read the name, it sees "\n" is already in the stream, and figures we must have previously entered an empty string! Definitely not what was intended.

We can amend the above program to use the std::ws input manipulator, to tell std::getline() to ignore any leading whitespace characters:

Now this program will function as intended.

Pick 1 or 2: 2
Now enter your name: Alex
Hello, Alex, you picked 2

Best practice

If using std::getline to read strings, use the std::ws input manipulator to ignore leading whitespace.

Key insight

Using the extraction operator (>>) with std::cin ignores leading whitespace.
std::getline does not ignore leading whitespace unless you use input manipulator std::ws.

String length

If we want to know how many characters are in a std::string, we can ask the std::string for its length. The syntax for doing this is different than you’ve seen before, but is pretty straightforward:

This prints:

Alex has 4 characters

Note that instead of asking for the string length as length(myName), we say myName.length(). The length() function isn’t a normal standalone function -- it’s a special type of function that belongs to std::string called a member function. We’ll cover member functions, including how to write your own, in more detail later.


std::string is complex, leveraging many language features that we haven’t covered yet. Fortunately, you don’t need to understand these complexities to use std::string for simple tasks, like basic string input and output. We encourage you to start experimenting with strings now, and we’ll cover additional string capabilities later.

Quiz time

Question #1

Write a program that asks the user to enter their full name and their age. As output, tell the user how many years they’ve lived for each letter in their name (for simplicity, count spaces as a letter).

Sample output:

Enter your full name: John Doe
Enter your age: 46
You've lived 5.75 years for each letter in your name.

Show Solution

4.13 -- Literals
4.11 -- Chars

608 comments to 4.12 — An introduction to std::string

  • Dudz

  • Thomas Kennings

    I've seen items linked together in std::cout using both "<<" and "+" now. For putting strings together, at least, is there a preferred or better choice between the two? Thanks.

    • nascardriver

      Don't use `+` when you're outputting to a stream. You'll construct a temporary, which is both slower and requires more memory than letting the stream take care of the formatting. That's what the stream is there for.

      Some types might not support streamed output, but support `+` with a `std::string`. Although I find that unlikely to be the case, it's possible that this is the reason why the code you saw didn't stream directly.

  • Thomas Kennings

    Hi everyone! This stupid little program I wrote above shouldn't let me enter my name, but it does! I haven't used the .ignore bit, but it doesn't seem to be taking the '\n' character like the examples show. Is this something happening specific to my machine? Any advice is appreciated!

    • nascardriver

      The line feed issue only occurs if you mix `std::cin >>` and `std::getline`. `std::cin >>` doesn't extract the line feed, which caused `std::getline` to not extract the next input.
      You're only using `std::getline`. That's fine.

  • Carl

    Hi There
    "To use strings in C++, we first need to #include the <string> header"

    In all of the foregoing examples and quiz I've not used the <string> header and programmes compile OK. Is this a compiler specific behaviour (Code::Blocks 16.01 revision 10692, OS specific (Linux Mint 19.2 Cinnamon Kernel 4.15.0-91) or something different? See the below code:-

  • Medhat Diab

  • furcag

    At first it was a little bit confusing how ignore 32767 work. Then changed it to number 5 and typed in numbers with spaces between them to see what happens.
    This helped me to understand what that value does. Check where number 3 goes :)

  • Lifthra

    I used static_cast only once as it needs only 1 double to make a double result (for the quiz), right?

  • vova220v

    How can I do somethig like this?

    • Alex

      use std::to_string()

  • salah


    if the std::cin captures "\n", then why "\n" did not assign in age ?? since it's in the input stream ?

    • Ben Synnock

      Can't '\n' be part of a string? It isn't in the input stream as its in your name string? In the example above the int is entered first, which can't hold '\n' and so it is left in the stream.

      In your code, if the name entered had a space (as in firstname lastname) then the lastname would end up in the age string and you wouldn't get a chance to enter the second input.

    • nascardriver

      `std::cin` captures the '\n', but it doesn't extract it (ie. put it into a variable). `std::cin >>` only uses the text before the next white space (eg. '\n') for extraction.
      `std::getline` on the other hand also extracts whitespace.

  • Nilesh

    If i use std::hex to print out hex form of a number it does so for the rest of the program. Is there any way to turn this off? See the last line of output where the length of final string should be printed out as 15 but it prints f.
    my code:


    • nascardriver

      You can explicitly go back to decimal by using `std::dec` in the same way you used `std::hex`. Alternatively, you can save the flags before you change them and restore them later.

  • EJW

    I can't figure out why this results in an infinite loop on  Please help.

    • nascardriver

      `std::cin` doesn't do anything until you tell it that you resolved the error. To do so, add a call to `std::cin.clear()` (Clears the error flag) before you call `ignore`.

  • raavann

    i tried removing #include <string>
    from the above examples,
    and they ran fine??

    • nascardriver

      Your might be including itself. This isn't guaranteed and your code won't work with other compilers. If you use something, include its header.

  • kavin

    myName.length() defaults to "unsigned int" because length is always a +ve value. So every time i have to use .length() for a calculation involving int, i have to use static_cast to convert it to int ?

  • Mercedes

    I don't know if this is the ideal way but its working I guess.
    Is there anything I should change?

  • Jake

    Hi Alex and company.

    My first draft of the code using getline and ignore looked like this:

      std::cout << "Enter your full name: ";   // Prompt for full name
      std::getline(std::cin, fullName);   // Get full name from user
      std::cin.ignore(10000, '\n');       // Flush new-line from the buffer

    When it ran it failed to prompt for age - it just hung.  ON impulse I just pressed <Enter> and got the age prompt.  I then commented out the line with the cin.ignore() call and it worked a I would have expected.  So I guess the release of VS I'm using (2019, Version 4.8.03761) does not leave the \n as warned in your section on "Mixing std::cin and std::getline()".

    Just thought that might be useful to know.

    • nascardriver

      `std::cin >>` leaves the '\n' in the input stream.
      `std::getline` doesn't.
      You only need to clear the stream if you use `std::cin >>` before `std::getline`, not the other way around.

  • Micael Starfire

    For ignore(), would this work to remove both the magic number and the difficult to read function call?

    and later calling ignore() with:

    Also, if not long, which data type should I be using for the constant?

    • nascardriver

      The type is `std::streamsize`. Use `constexpr` for compile-time constants.

      You're right, this removes the magic number and the long function call.

  • Ged

    This code works as well. Why do we need another variable for myName.length()? Isn't it already and integer type? If it's lower than int won't it just get promoted?

    • nascardriver

      We don't need a separate variable for the length, we also don't need one for the result, but having them there can make the code easier to follow for beginners.

      - Initialize your variables with brace initializers.
      - If your program prints anything, the last thing it prints should be a line feed ('\n').

  • Jesse

    In the solution for the quiz, I notice that all of the program is in the main() function.
    Through these lessons, it was my understanding that we wanted to have functions do as little as possible, per function.
    In my code, I have done such, which has made it much, much, longer, though it still does exactly the same thing.
    My question is, would my code, while understandly not as efficient, be considered "wrong?"
    [Code] #include <string>
    #include <iostream>

    std::string getUserName() {
        std::cout << "Please enter your full name. \n";
        std::string userName{};
        std::getline(std::cin, userName);
        std::cin.ignore(32767, '\n');
        return userName;

    float getUserAge() {
        std::cout << "Please enter your age. \n";
        float userAge {};
        std::cin >> userAge;
        return userAge;

    float calculateYearsPerLetters(float nameLength, float userAge) {
        float yearsPerLttrs { userAge / nameLength };
        return yearsPerLttrs;

    int printResults(float yrsPerLtrs, float userAge, std::string userName) {
        std::cout << "You full name is: " << userName << ". \n";
        std::cout << "You are " << userAge << " years old. \n";
        std::cout << "You have lived " << yrsPerLtrs << " years for each letter in your name. \n";

    int main() {
        std::string userName { getUserName() };
        float userAge { getUserAge() };
        float nameLength { static_cast<float>(userName.length()) };
        float yrsPerLtrs { calculateYearsPerLetters(nameLength, userAge) };
        printResults(yrsPerLtrs, userAge, userName);
        return 0;

    • nascardriver


      Closing code tags use a forward slash (/)

      - Inconsistent formatting. Use your editor's auto-formatting feature.
      - `printResults` is missing a return statement. Your compiler should've warned you. Make sure you enabled all warnings and read them.
      - You don't need `yearsPerLttrs`, you can return right away.

      > would my code, while understandly not as efficient, be considered "wrong?"
      No, not at all. Your code is easier to maintain and more reusable than the quiz's solution.

  • HangUp

    Your "Rule: If reading values with std::cin, it’s a good idea to remove the extraneous newline using std::cin.ignore()." is missing a green box around it

  • Mike

    I noticed for the quiz you didn't use the usually recommended uniform initialization for the following statement:

    int letters = name.length();

    If I use uniform initialization, I get an error b/c the value of name.length is unsigned, while int is signed. Ok, that's fine! But if I use direct or copy, then there is no error.

    Why is this?

    Name.length() is still unsigned, and int is still signed. How does using copy or direct initialization change this behavior?

    • Brace initialization enforces stricter type conversion rules than copy and direct initialization. It doesn't implicitly change the signedness.
      Either add a `static_cast<int>` or declare `letters` as `std::size_t`.

      • Mike

        Ah, I should've remembered that as it was discussed way back in a previous lesson. I've just got in the habit of using uniform initialization by default, and unfortunately, just forgot why I was using them. Thanks for the reminder and all your help!

  • HurricaneHarry


    After testing a bit the quirks of cin, cin.ignore() and getline(), it's where I could need a helping hand understanding the "why" of the resulsts. Using codeblocks 17.12, the code of the examples reacts a tad differently for me.

    The example after "cin may yield some surprises", actually yields less surprise for me regarding the output, as it works as expected just culling the second name:

    Enter your full name: John Doe
    Enter your age: 88
    Your name is John and your age is 88

    That's the result I get. If then I substitute the "cin-line" of the name-part with the "getline()-line", like in the example after, I receive the result of the first example (no second prompt).
    If then I add an additional line containing a cin.ignore() like:

    "std::cin.ignore(30000, '\n');"

    it works like your second example. I checked my settings like described in the first chapter and it's set. Still I don't really understand the behaviour.

    Similar it goes for the appearance of a prompt regarding cin.ignore. As it sometimes produces a prompt and sometimes not. After testing my guess is:

    If I make use of cin and enter simple input like a number, cin actually queues not just the number but also the "enter character" / '\n' used to activate it inside, and just delivers back out the number, keeping the '\n'-char. That would explain the getline() behaviour as it streams the '\n' the moment the queue is used again, cancelling the input as it receives the '\n' without userinput from the queue.
    Sadly, this doesn't explain the behaviour of using solely cin without getline().
    And finally if the queue is empty the cin.ignore() has nothing to work on and calls a cin input before going on.

    maybe someone can help me shedding a bit of light on that matter.
    Thanks in advance!

    • Alex

      I'm not sure why you're getting a different outcome. I've tested the code on quite a few different compilers and none of them produced what you're seeing. Anybody else seeing the same outcome?

      • HurricaneHarry

        Thanks for the comment.

        Searched the web for quite some time to find some more indephth documentation on the workflow of cin and cin.ignore, but only found the ever same standard stuff n examples.

        But did I get the described normal worklflow right regarding those? (insert and extraction stuff)
        Also that both only create a user prompt if the queue is empty and otherwise simply stream in the remainders in the queue?

        Right now using getline() and cin.ignore() in tandem does work as needed, and only seems to produce one deficiency: if used together with the last call for cin it produces 2 prompts to close/finish the program instead of just one.
        (I need to use the small workaround you described in the beginning to delay the closing of the window)
        Still... hate using stuff I don't fully understand.

        Thanks again, for your engagement and a great project!

        • Your flow explanation is correct.
          `std::cin >>` leaves the trailing line feed in the input stream.
          `std::getline` extracts and discards the trailing line feed.
          If you call `std::cin.ignore` on an empty stream, it will block until there is something in the stream (ie. you have to write something and press enter).
          Same for `std::cin >>` and `std::getline`. If there is something in the input stream already, that data will be used (That's what the "John Doe" example tries to show).

          As for your extraction issue, see if you can change or update your compiler. What you're experiencing seems like non-standard behavior.
          If changing the compiler doesn't help, run your program in a terminal that you opened manually (On Windows: Shift + Right Click in the executable's directory -> "Open Command Prompt (Or powershell) here" ->  then enter the executables name). If this fixes it, your IDE is messing up the input.

  • Parsa

    Should we always be favouring std::getline() over std::cin?

  • White Russsian

    I have put together this little cin/cout exercise and am struggling to understand why when I enter anything other than an integer (e.g. a letter A-Z) on the first prompt, this programme start racing whereas if I enter an integer, it moves on to the next step, asking me whether I would like to enter another integer, as intended. Any ideas what's going on?


    Please enter integer: 5
    You entered: 5
    Enter another integer? (y/n): y
    Choice is : y
    Please enter integer: H
    You entered: 0
    Enter another integer? (y/n): Choice is : y
    Please enter integer: You entered: 0
    Enter another integer? (y/n): Choice is : y
    Please enter integer: You entered: 0
    Enter another integer? (y/n): Choice is : y

  • Hai Linh

    Aside from using a magic number (32767), one could also declare a static const (or constexpr) int storing the max streamsize.

    Additionally, we can use a typedef (or type alias):


    Note that we still have to call function max() (streamsize::max())

  • VerticalLimit

    Hi Nascardriver
    Any suggestions to make it better ?

    Quiz 1

    • - Line 12: Initialize with empty curly braces. Only explicitly initialize if you're using the value.
      - Line 15: Brace initialization.
      - Line 15: Don't use C-style casts, they're unsafe.
      - Limit your lines to 80 characters in length for better readability.

  • Piyush Pranjal

    if this function call, what we get.

    My expectation to get, Underweight, Normal, Over Weight.

    Am I right or wrong?

    Because my program breaks when this function's call occurred.

    • Your function has to return, no matter what happens.
      If `x < 16.0f` or `x > 40.0f` your function doesn't return.

      You don't need all those duplicate comparisons, use `else if`.

      • Piyush

        Thanks Bro.
        Successfully updated my code and getting unknown at this moment.
        Now I need to find what is wrong with the argument.

        • You don't need to compare `x` to `18.5f` in line 7 or `25.0f` in line 11, you already checked those.

          • Piyush

            What happens if we get 6.0 as a parameter value. So, 9th line will be executed or not?

            if this function gets 6.0 as parameter value than 9th line will be executed or not?

  • Parsa

    Is there a particular reason why string variables are initialized using parenthesis?

  • Parsa

    Is the string library really necessary?

    The type string is already defined for me without the library.

  • Samira Ferdi

    Hi, Alex and Nascardriver!

    My compiler throw an error if I do this:

    But, if I do this:

    My program works! Why is that?

    • Assuming `name` is an `std::string`.
      `std::string::length` return an `std::size_t`, which is an unsigned integer type. Converting and unsigned integer to signed causes loss of precision.
      Add a `static_cast<int>`. Use brace initialization.

      • Samira Ferdi

        Thanks, for reply!

        So, you mean is


        My next question is why 32767 in std::ignore(32767, '\n')?
        Why don't let's say 30, 300, 230 or 1?

        • The first version.
          Don't pass 32767 to `std::cin.ignore`. It's a magic number Alex made up because he's too lazy to type. It doesn't have any special meaning to the function.
          Pass `std::numeric_limits<std::streamsize>::max()`. It causes `std::cin.ignore` to ignore everything until the specified character ('\n') is found.

  • Grego

    Hello All,

    In "Appending strings" you state that "You can use operator+ to concatenate two strings together, or operator+= to append one string to another.", however in the following code block, you say this:

    Concatenate and append are both used when referring to '+' operator.
    Hence my question is : Should I worry about the functional difference between the two at this point and if yes, what is it?

Leave a Comment

Put all code inside code tags: [code]your code here[/code]