S.4.4b — An introduction to std::string

What is a 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!”) are placed between double quotes to identify them as a string.

Because strings are commonly used in programs, most modern languages include a built-in string data type. C++ includes one, not as part of the core language, but as part of the standard library.


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 input and output

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

This prints:

My name is: Alex

However, 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 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, waiting for the next extraction. When we then used operator>> to get variable age, it extracted “Doe” instead of waiting for us to input an age. We are never given a chance to enter an age.

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

Mixing std::cin and std::getline()

Reading inputs with both std::cin and std::getline may cause some unexpected behavior. Consider the following:

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” line, and then exits. What happened?

It turns out, when you enter a value using cin, cin not only captures the value, it also captures the newline. So when we enter 2, cin actually gets the string “2\n”. It then extracts the 2 to variable choice, leaving the newline stuck in the input stream. Then, when std::getline() goes to read the name, it sees “\n” is already in the stream, and figures we must have entered an empty string! Definitely not what was intended.

A good rule of thumb is that after reading a value with std::cin, remove the newline from the stream. This can be done using the following:

If we insert this line directly after reading variable choice, the extraneous newline will be removed from the stream, and the program will work as expected!

Rule: If reading values with std::cin, it’s a good idea to remove the extraneous newline using std::cin.ignore().

What’s that 32767 magic number in your code?

That tells std::cin.ignore() how many characters to ignore up to. We picked that number because it’s the largest signed value guaranteed to fit in a (2-byte) integer on all platforms.

Technically, the correct way to ignore an unlimited amount of input is as follows:

But this requires remembering (or looking up) that horrendous line of code, as well as remembering what header to include. Most of the time you won’t need to ignore more than a line or two of buffered input, so for practical purposes, 32767 works about as well, and has the benefit of being something you can actually remember in your head.

Throughout these tutorials, we use 32767 for this reason. However, it’s your choice of whether you want to do it the “obscure, complex, and correct” way or the “easy, practical, but not ideal” way.

Appending strings

You can use operator+ to concatenate two strings together, or operator+= to append one string to another.

Here’s an example of both, also showing what happens if you try to use operator+ to add two numeric strings together:

This prints:

45 volts

Note that operator+ concatenated the strings “45” and “11” into “4511”. It did not add them as numbers.

String length

If we want to know how long a string is, we can ask the 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 like we’ve used up to this point -- 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. It also has a lot of other capabilities that we haven’t touched on here. 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.


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.

Quiz solutions

1) Show Solution

S.4.5 -- Enumerated types
S.4.4a -- Explicit type conversion (casting)

419 comments to S.4.4b — An introduction to std::string

  • learning

    Hey, why does this work even if limits isn't included?:

    • Because <limits> was included by some other header that you included. Don't rely on transitive includes. If you use something, include the header that declares it.

      • learning

        Thanks for your reply, could you also please check my code?
        This was the first one:

        And this the second one (I decided to split it into functions):

        Also which one would you prefer? Thanks in advance!

        • Snippet 1
          - Line 17: Yes, but your comment broke the limit :/

          Snippet 2
          - Line 18: In this specific case, no. You're not asking for any more input after this line, so there's no need to clear it. If you were to swap line 24 and 25, you'd need it.
          - You're using the same name style for variables and functions, this can lead to confusion.

          I prefer your second version. It's easy to understand what the program does just by reading `main`.

          • learning

            "- You're using the same name style for variables and functions, this can lead to confusion."
            Do you mean these variables:

            If so, can you give me a example how I should name my variables?

            • All of them. You use camel case with a lower case first letter for both functions and variables.
              I use a variation of hungarian notation, where all variables get prefixed by an abbreviation of their type. I name functions just like you do.

              You can also use an upper case first letter (Usually done for types) with camel case, or underscores (This will only differ from you current convention if a name consists of multiple parts).
              I doesn't matter what you choose, as long as you can tell things (types, variables, function, etc.) apart and you're using the style consistently.

              • learning

                Okay, and should I name my variables inside functions different than the ones in main? For example:

  • While just messing around, I tried using direct initialization to define int letters by feeding it name.length() as a parameter, which I thought might work, but it came back with this error... "conversion from 'unsigned int' to 'int' requires a narrowing conversion"

    EDIT: Never mind, this question has been asked and answered already. Thanks for keeping the website updated, fellas!

    • For the sake of future readers coming across your question, `std::string::length` returns a `std::size_t`, which is most often implemented as an `unsigned int`. Brace initialization doesn't allow narrowing casts, so your compiler errors out. To fix this, add a `static_cast` to `int`.

  • Sweta

    Is there a reason we are using direct initialisation for strings instead of brace inititalisation?

  • Napalm

    So mine came out smaller than the answer and although it works it makes me suspect I missed something. Would it be better to not declare age as a double? It did sort of feel a bit clumsy as age is nearly always used as an int.

    I also noticed I couldn't do this

    but not sure why.

    • Your code is shorter, because you did the calculation in-line, which is fine.
      Whether you implement the age as an int or double is up to you.

      > I also noticed I couldn't do this
      @std::getline doesn't return the string it reads, it returns the stream (@std::cin).

  • Jonas


    Really love your tutorial.

    How come I can't use

    "int letters{ name.length() };"


    "int letters = name.length();"

    works perfectly fine? The first one produces "conversion from 'unsigned int' to 'int' requires a narrowing conversion", which I solved using "int letters{ static_cast<int>(name.length()) };".

    All the best.

    • Brace initialization prevents narrowing conversions.
      @name.length() returns an @std::size_t, which is an unsigned integer type.
      An unsigned int can store higher numbers than a signed int, those high numbers would turn into negative numbers when cast into a signed int.

  • Hugh

    Hi there guys, love the website.

    My code works fine but I'd appreciate any constructive criticism, especially with regards to my naming of variables. If anyone has any guidelines they can give me for naming I'd appreciate that too..
    Also, I know that there are a few places where I could shorten my code, example defining a variable and then manipulating it in a separate line. That was on purpose to make it easier to watch my variables in the debugger.

    I did originally have this not accounting for white space however @nascardriver I saw your comment to Hampus and wanted to see if I could get std::count to work. Thanks for that tip!

    I have my variable reading the string length as an unsigned int as it gave me errors when trying to convert it to a regular integer.

    • Hi Hugh!

      * Line 27: Initialize your variables with brace initializers.
      * Line 34: Limit your lines to 80 characters in length for better readability on small displays.
      * Line 18: Don't pass 32767 to @std::cin.ignore. Pass @std::numeric_limits<std::streamsize>::max().
      * @std::string::length returns an @std::size_t, not unsigned int (The 2 types might be the same on your system, but not for others). Same for @std::count.

      Your variable names are fine.

      • Hugh

        Hi! Thank you so much!!

      • Thomas

        You are both wrong.

        WIN64 typedef unsigned long long size_t
        WIN32 typedef unsigned int (short / long) size_t

        std::string::length's return type is std::string::size_type.

        • You found 2 examples of a definition. Examples don't prove anything. All that is stated about @std::size_t is
          "The type size_t is an implementation-defined unsigned integer type that is large enough to contain the size
          in bytes of any object"
          N4810 § 17.2.5 (3)
          Compilers are free to do whatever they want as long as the above quote is true. It might be an unsigned long for you, but that's not how it is everywhere else. Using @std::size_t guarantees compatibility with all compilers.

  • Benur21

    Can I do like

    so it is even easier to remember the number?

    • Alex

      The only hesitation I have about this is that there might be some compiler somewhere where streamsize is a 2-byte signed integer, and 99999 overflows. 32767 was picked to ensure non-overflow. Given that, 9999 might be a better choice.

  • weedfox

    mostly @ nascardriver for suggestions:

    • * Line 13, 23, 24: Initialize your variables with brace initializers. You used copy initialization.
      * Line 6: Initialize your variables with brace initializers.
      * Line 19: Limit your lines to 80 characters in length for better readability on small displays.

  • Rokki

    is it okay to initialize a double type variable like this ?   "double age { 0.0f };"

    • Alex

      Will it work? Yes. Should you do it this way? No.

      Initialize your variable with literals of the same type.

  • Hampus

    But by using different functions "std::cin.ignore" is not needed since the cin terminates as the function terminates.

    Its only useful if doing severeal "std::cin" within the same function right?

    More reasons to seperate as much code as possible into several functions becouse i prefer doing things the "correct way" and not the "easy way"

    is a bit annoying to type up XD

    Im totally new to c++ so my code might not be awesome, but the tutorial didn't specify that cin buffer terminates with the function it belongs to (even thou it is completely logical for me im pretty sure it might not be for all)

    Oh and one could simply subtract "1" from "m.fullName.length() for a result without namespace counted as a char(which ofcourse the tutorial writer knows, but i just wanted to include this, i didn't include it in code cause the tutorial specificly said to care about that in this quiz)

    (seems if i edit my post after i posted the "code" / "/code" doesn't work..)
    (might be worthwile for you guys to know this, guessing its a bug in the website)
    (simply removed post and added a new one now to include my edits with "code" / "/code" working]
    (didn't include the brackets for "code" since website thought i was gonna write code if i did ;P)

    • * Line 4, 7, 8, 10, 12, 17, 18, 20, 23, 25, 30, 32: Limit your lines to 80 characters in length for better readability on small displays.
      * You're using the same name style for variables and functions, this can lead to confusion. Your variables in @main don't follow your naming convention in the other 3 functions and are misleading, because the "m_" prefix is usually used for member variables (covered later).
      * @agePerLetter's parameters are poorly named. Use descriptive names. Variable names can be re-used in other functions. They can have the same name as in @main.

      > the cin terminates as the function terminates
      @std::cin is not affected by the function it is used in. It persists across function calls. Your comments about its buffer are wrong.
      @std::cin.getline doesn't leave anything in the input stream (assuming you're inputting via the console), so there's no need to use @std::cin.ignore. @std::cin::operator>> can leave data in the input stream. If you don't want that, you need to use @std::cin.ignore.

      > one could simply subtract "1"
      This won't work for people with middle names or single-word names. You can use @std::count from the @<algorithm> header to count occurrences of an element in a container (Including @std::string).

    • Hampus

      I was assuming the quiz/test was only for First and Last name as it was exampled. :)
      Thats why -1 would work in this particular example and otherwise it would obviously be incorrect.

      I'm guessing we learn in later tutorials how to pick out how many "spaces" are in a text to then remove the correct amount.

      I don't use the comments like this in my actual code, i wrote it for posting here and would never fill my codes with all that jibberish.

      I just wanted to explicitly explain what i intended with each line i made. :)

      If co-writing a code i would obviously follow common variables most people use, the ones i made makes more sense to me (for the moment since this is where i am in the tutorial and i cannot know how things are supposed to be done until i get there in the tutorial).

      Then i understand its better to do correct variable names from the beginning of learning, however it makes learning harder for the moment and i figure i'll adapt to that as i progress in the learning process.

      Except for that i think your comments were very useful and thanks for taking your time to go thru my coding.

      I misunderstood the reason for using the ignore command completely, i didn't follow the "Don't code when tired" rule of thumb when i was writing all this hehe. =)

      All in all my code was "decent" ?
      Ofcourse things can be optimized and done better.
      Considering where in the tutorial we are so far i mean.

      I love how you try to reply to all comments in these tutorial, i learn almost as much from the comments as i learn from the tutorial themselves. :)

      GREAT work done on these tutorials!

      You guys wouldn't happen to have some kind of irc channel or so to discuss tutorials/coding in ? =)

      And for me it didn't seem like the \n persisted thru other cin's i made ?
      I mean the code compiled and worked just fine O_o

      oooooh.... now i see, i first used getline(which as you said didn't leave a \n in the cin buffer, and THEN i used the std::cin command which actually left a \n in the buffer throughout the code, but since i didn't use cin again it didn't cause a problem...

      Now i understand, sorry as i said i was tired, and should've used the ignore command for that(as common practice) to not cause an error if i were to use another cin command.

      Again, thanks for replying, helps a lot with the understanding.

      Think im gonna terrorize you more and post on more tutorials. :P

      • Apart from the things I pointed out your code is fine.

        > seems if i edit my post after i posted the "code" / "/code" doesn't work
        That's a client-side issue. Once you refresh the page, the code highlighting works again.

        • Hampus

          Oh you already replied lol, fast one XD

          Oh well that's good to know.
          I figured i'd have some things that needed to be pointed out, and thanks for clearing up some misunderstandings.

          Just knowing that the code is fine(except for what you pointed out) is good to know, since i have no idea if im doing things stupidly complicated or not haha.

          Damn i was so annoyed when editing the post by deleting and making a new post cause i didn't even try to refresh lol. ;P
          Good to know.

  • cmb

    II'm currently reading a book on C++, and they discuss the issue with the newline character still being in the input stream as well. However, they approach it differently, wherein the author recommends calling the getline() function with empty parameters (wherever it makes sense to put it, i.e after using std::cin). The logic behind it, being that it "eats" the newline character, thus taking it out of the input stream. To me this method, and using the ignore function achieve the same thing. Is there some benefit I'm not quite seeing, as far as using the ignore function goes, over the one the book mentions?

  • Jorge Guimarães

    On Visual Studio 2019 / Windows 10,

    outputs 2^63 (a really really big number)

  • Kol

    For the appending strings, is it supposed to print "45 volts" or "45volts" because I'm getting the latter?

  • Aditi

    Hello ,

    Can you please elaborate on how std::cin works as an object and calls the getline() function ? I always thought of it as a command under the iostream library that is used to extract an input from the user. I cannot understand how it works like a class object and calls a function of the class .

    Thank you !

  • Darshan


    Even after

    is it a good practise to use cin.ignore,
    or is it only used after

    Thank you

  • Arthur

    here is what I wrote , I am unclear in the answer why initialize variables 'age' and 'letters as integers then change age into a double? I understand the rounding and math but don't understand if there is a reason not to initialize them as doubles in the first place?

    I am still a little unclear on the types of numbers and when to use them but like the write a program quizzes so haven't gone back to review yet. I experimented with the variables age and agePerLetter as int or one of either being double while the other was int and only with both as integer the rounding happened , I am wondering if doing it this way is chancing 'undefined behavior' or something?

    • ethano

      Honestly, my program worked with them uninitialized. You can initialize them, but that isn't technically needed. As long as they're doubles, you are most likely fine.

  • J


    Here's what I came up with. Any tip/suggestions?

  • Alireza

    Hello and thanks,
    So what about these kinds of strings ??

  • Paulo Filipe

    My solution to the quiz for some helpful review:

    I did it this way to practice previous taught material, and I have a question.
    Is there some way to pass line 24 to the printText() function that I created?


    • Hi Paulo!

      * Line 6: @std::string's default constructor is faster than the constructor that accepts a string literal. Use empty curly brackets.
      * Line 24: Limit your lines to 80 characters in length for better readability on small displays.
      * Line 23: Don't pass 32767 to @std::cin.ignore. Pass @std::numeric_limits<std::streamsize>::max().

      > Is there some way to pass line 24 to the printText() function that I created?
      @std::string can be concatenated with +. Numbers can be converted to a string via @std::to_string.

      • Paulo Filipe

        Helpful helpful helpful!

        Thank you!

        BTW, I am reading a book called c++ primer 5th edition alongside learcpp lessons. Do you guys think it's something that I should be doing? The pace of the book is different than learcpp lessons, and at the very beginning of the book we are presented with while and for loops. Your lessons are very detailed.

        And do you guys know why people at r/learnprogramming say that learncpp is a bad place to learn c++? I am finding these lessons very good!

        • I don't know that book. If you feel like it helps you, keep reading it.

          > learnprogramming
          I've commented on this question here

        • Alex

          Loops are where programs start to get interesting, so a lot of authors like to rush into that topic, so the examples can be more fun and the programs less trivial.

          I've though about moving the loops related content up, but that would mean splitting other topics up (e.g. to do loops, you at least need an understanding of bool and int and relational operators -- so I'd have to cover those, do loops, then come back to all the other data types and operators -- that seems more confusing than helpful).

  • Dear Teacher,

    Please let me say you regarding program with line "std::cin.ignore(32767, '\n');" that even with number "1" (std::cin.ignore(1, '\n');) it runs. However with "0" it does not.

    With regards and friendship
    Georges Theodosiou

  • Benur21

    We could also do

    , right?

  • Benur21

    "return 0;" is missing in two code blocks about John Doe

  • Iury

    Here's my code for the quiz, I used static_cast instead of  creating a  variable to store the name's lenght, this way it doesn't count the spaces as name lenght (forgot the simplicity lol).

    • * Line 8: Initialize your variables with brace initializers.

      > this way it doesn't count the spaces as name lenght
      The cast has no effect on the spaces. Your program counts spaces towards the length.

  • Nathan

    Could you compare a string of numbers, such as
    std::string a = 1234
    if (a == 1234)
    If not, how could I do this?

    For context I have two number I want to combine into a string thing so it is easier to check it.
    Insted of if ((x == 56 && y == 24) || ( etc

  • n1fty

  • Diana

    Hello again, it might be a dumb question but in the quiz solution line 11 what does the {0} mean?

  • Diana

    Hi! This code is the same as one piece of code in this tutorial, except I added std::cin.get() so that the console won't close right after the "Hello" sentence is printed, until I hit enter. I have been doing this all the time with Visual Studio 2017 following the previous chapters. But in the code below I need to hit enter twice to get the "Hello" sentence printed. Any idea on why this happens? Thanks.  

    • * Line 10, 16: Initialize your variables with uniform initialization.
      * Don't pass 32767 to @std::cin.ignore. Pass @std::numeric_limits<std::streamsize>::max().

      There's one wait in line 17, one in line 18 and another one in line 24. What you described is the expected behavior.

      • Diana

        Thank you for your reply. For the uniform initialization, is

        okay or the below is better? And why do I need to initialize even though the uninitialized code also works?

        • If there is an appropriate 0-value for a type, use that. If there is none, leave the brackets empty. You could initialize strings to "", but doing so would call the std::string::string(const char*) constructor, which is slower than the default constructor while doing the same thing.

          Both @std::cin::operator>> and @std::getline override the variables in every case. Initializing them doesn't make a difference here (Assuming C++11 and later). However, placing your variables in a predictable state and any point in time makes debugging easier and prevents forgetting an initialization when you would have needed it.

  • l1b3rator


    int main()
        std::cout << "Enter your name: ";
        std::string name;
        std::getline(std::cin, name);
        std::cout << "\n";

        std::cout << "What is your age: ";
        double age;
        std::cin >> age;
        std::cout << "\n";

        std::cout << name << " you have lived " << age / name.length() <<" years for each letter in your name"<< std::endl;

        return 0;

    But i've tried to make a function (outside of main) that would return a string and i would like to call that function from main. How do i create a function like that? is it possible?

    • l1b3rator

      Ignore the request, i have found the answer:

      std::string getName()
          std::cout << "Enter your name: ";
          std::string name;
          std::getline(std::cin, name);
          std::cout << "\n";

          return name;

      double getAge()
          std::cout << "What is your age: ";
          double age;
          std::cin >> age;
          std::cout << "\n";

          return age;
      int main()
          std::string name = getName();
          double age = getAge();

          std::cout << name << " you have lived " << age / name.length() <<" years for each letter in your name"<< std::endl;

          return 0;

  • Erk_Forever

    Why not just make age a float or double out of the gate and divide age by name.length? It cuts two variables. Am I missing something here? Is code::Blocks filling in the gaps here on something?

    • Hi!

      * Line 7: @std::string's default constructor is faster
      * Line 11: Use float literals for floats (0.0f instead of 0)
      * Line 14: Limit your lines to 80 characters in length for better readability on small displays.

      One typically uses whole numbers to describe age, I guess that's what made Alex use an int. There's nothing wrong with using a floating point type.

  • Gacrux

    Why if #include <string> is removed, your example below still compiles fine?

    Is #include <string> really necessary?

    I'm using Code::Blocks 17.12 with all the settings described in the first chapters.

    • <iostream> includes <string>. If your remove line 1, 7, 11 your code won't compile. Don't rely on transitive includes, they can cause your code to break in files you're not even working on. If you need something, explicitly include it's header.

      @main is missing a return statement.

Leave a Comment

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