6.6 — C-style strings

In lesson 4.4b -- An introduction to std::string, we defined a string as a collection of sequential characters, such as “Hello, world!”. Strings are the primary way in which we work with text in C++, and std::string makes working with strings in C++ easy.

Modern C++ supports two different types of strings: std::string (as part of the standard library), and C-style strings (natively, as inherited from the C language). It turns out that std::string is implemented using C-style strings. In this lesson, we’ll take a closer look at C-style strings.

C-style strings

A C-style string is simply an array of characters that uses a null terminator. A null terminator is a special character (‘\0’, ascii code 0) used to indicate the end of the string. More generically, A C-style string is called a null-terminated string.

To define a C-style string, simply declare a char array and initialize it with a string literal:

Although “string” only has 6 letters, C++ automatically adds a null terminator to the end of the string for us (we don’t need to include it ourselves). Consequently, myString is actually an array of length 7!

We can see the evidence of this in the following program, which prints out the length of the string, and then the ASCII values of all of the characters:

This produces the result:

string has 7 characters.
115 116 114 105 110 103 0

That 0 is the ASCII code of the null terminator that has been appended to the end of the string.

When declaring strings in this manner, it is a good idea to use [] and let the compiler calculate the length of the array. That way if you change the string later, you won’t have to manually adjust the array length.

One important point to note is that C-style strings follow all the same rules as arrays. This means you can initialize the string upon creation, but you can not assign values to it using the assignment operator after that!

Since C-style strings are arrays, you can use the [] operator to change individual characters in the string:

This program prints:


When printing a C-style string, std::cout prints characters until it encounters the null terminator. If you accidentally overwrite the null terminator in a string (e.g. by assigning something to myString[6]), you’ll not only get all the characters in the string, but std::cout will just keep printing everything in adjacent memory slots until it happens to hit a 0!

Note that it’s fine if the array is larger than the string it contains:

In this case, the string “Alex” will be printed, and std::cout will stop at the null terminator. The rest of the characters in the array are ignored.

C-style strings and std::cin

There are many cases where we don’t know in advance how long our string is going to be. For example, consider the problem of writing a program where we need to ask the user to enter their name. How long is their name? We don’t know until they enter it!

In this case, we can declare an array larger than we need:

In the above program, we’ve allocated an array of 255 characters to name, guessing that the user will not enter this many characters. Although this is commonly seen in C/C++ programming, it is poor programming practice, because nothing is stopping the user from entering more than 255 characters (either unintentionally, or maliciously).

The recommended way of reading C-style strings using std::cin is as follows:

This call to cin.getline() will read up to 254 characters into name (leaving room for the null terminator!). Any excess characters will be discarded. In this way, we guarantee that we will not overflow the array!

Manipulating C-style strings

C++ provides many functions to manipulate C-style strings as part of the <cstring> header. Here are a few of the most useful:

strcpy() allows you to copy a string to another string. More commonly, this is used to assign a value to a string:

However, strcpy() can easily cause array overflows if you’re not careful! In the following program, dest isn’t big enough to hold the entire string, so array overflow results.

Many programmers recommend using strncpy() instead, which allows you to specify the size of the buffer, and ensures overflow doesn’t occur. Unfortunately, strncpy() doesn’t ensure strings are null terminated, which still leaves plenty of room for array overflow.

In C++11, strcpy_s() is preferred, which adds a new parameter to define the size of the destination. However, not all compilers support this function, and to use it, you have to define __STDC_WANT_LIB_EXT1__ with integer value 1.

Because not all compilers support strcpy_s(), strlcpy() is a popular alternative -- even though it’s non-standard, and thus not included in a lot of compilers. It also has its own set of issues. In short, there’s no universally recommended solution here if you need to copy C-style string.

Another useful function is the strlen() function, which returns the length of the C-style string (without the null terminator).

The above example prints:

My name is: Alex
Alex has 4 letters.
Alex has 20 characters in the array.

Note the difference between strlen() and std::size(). strlen() prints the number of characters before the null terminator, whereas std::size (or the sizeof() trick) returns the size of the entire array, regardless of what’s in it.

Other useful functions:
strcat() -- Appends one string to another (dangerous)
strncat() -- Appends one string to another (with buffer length check)
strcmp() -- Compare two strings (returns 0 if equal)
strncmp() -- Compare two strings up to a specific number of characters (returns 0 if equal)

Here’s an example program using some of the concepts in this lesson:

Note that we put strlen(buffer) outside the loop so that the string length is only calculated once, not every time the loop condition is checked.

Don’t use C-style strings

It is important to know about C-style strings because they are used in a lot of code. However, now that we’ve explained how they work, we’re going to recommend that you avoid them altogether whenever possible! Unless you have a specific, compelling reason to use C-style strings, use std::string (defined in the <string> header) instead. std::string is easier, safer, and more flexible. In the rare case that you do need to work with fixed buffer sizes and C-style strings (e.g. for memory-limited devices), we’d recommend using a well-tested 3rd party string library designed for the purpose, or std::string_view, which is covered in the next lesson, instead.


Use std::string or std::string_view (next lesson) instead of C-style strings.

6.6a -- An introduction to std::string_view
6.5 -- Multidimensional Arrays

218 comments to 6.6 — C-style strings

  • I saw this usage on the internet but i didnt understand !

    please explain this!

  • sims7564

    I need some help understanding std::cin.getline() and member functions in general (that's what the .getline() part is named right?).

    If I understand correctly, ".getline" is a member function of std::cin. But when I go on cppreference to get more info on std::cin.getline(), I get nothing. I can find std::cin, but it does not mention what member functions I can use with std::cin (we learned ".ignore", ".clear" and some other from previous lessons). I can find std::getline as a stand-alone function in cppreference, but how do we know that we can use it with std::cin? Is it because both work on the input stream that we can use them together? Is the period "." a common operator to "combine" different functions that work on the same input?

    Thanks in advance for your response.

    • nascardriver

      `std::getline` and `std::cin.getline` are different functions, there's no connection between the two.

      On cppreference, open the page of `std::cin`

      Look at its declaration at the very top

      `std::cin` is of type `std::istream`, click `std::istream`

      There you can see all of the member functions, including `std::basic_istream::getline`

  • Manish

    I kind of played around the property of cout printing every char in memory until it encounters a null char if a string is tried to print using cout.

    It's always printing 'Alexx'. I don't think its a coincidence.

  • Pioner

    In the second example where it prints the ASCII codes, I think it's better to use ' ' instead of " ".

  • mqu

    > C++ provides many functions to manipulate C-style strings as part of the <cstring> library.

    Shouldn't it be <cstring> header, as there's no such thing as <cstring> library?

  • Hello Everyone! I use the std::strcat to do some strange thing and get strange result can you help analize the result.

    strcat is dangrous. when I try strcat the Hello to temp. the temp is a char[5] without any empty space.
    but I get result

    I use the clang compiler
    Apple clang version 11.0.3 (clang-1103.0.32.62)
    Target: x86_64-apple-darwin19.4.0

    this is the code of strcat.

    • nascardriver

      The first parameter of `std::strcat` is the destination. You're appending `Hello` to `temp`.

      • I think there is no more space for temp to store the hello. what can strcat do to. maybe the Memroy of temp was free and remalloc.

        • nascardriver

          `strcat` doesn't free or allocate anything. There wasn't enough space to append the string, so you're getting undefined behavior. It's the callers responsibility to allocate memory.

  • Haider

    In line 9, I think there should be a space after "std::cout << "myString".

  • salah

    Why character array will end up with NULL terminator \0 and the other arrays like int arrays don't ??

    • nascardriver

      Character arrays aren't zero-terminated by nature. Only the string literals are, because there needs to be a way of knowing when the string ends. When you create a character array from a string literal, all characters are copied into the array, including the zero-terminator.

  • Wambui

    #include <iostream>
    #include <iterator> // for std::size

    int main()
        char name[255]; // declare array large enough to hold 255 characters
        std::cout << "Enter your name: ";
        std::cin.getline(name, std::size(name));
        std::cout << "You entered: " << name << '\n';

        return 0;

    how can the line " std::cin.getline(name, std::size(name));" be written in C++11?

    • nascardriver

      Without `std::size`, you can get an array's length via

      Since `name` is an array of `char`, which has size 1, the division has no effect (Because x/1 = x).

  • Oscar

    Hi, is there a reason why we choose the number 255 here? I feel that this has to do with 2 to the power of 8 is 256, so using 255 can lead to some sort of optimization? But why do we choose 255 but not 256? Thanks in advance!

  • Bruno

    >> Because not all compilers support strcpy_s(), strcpy() is a popular alternative -- even though it’s non-standard, and thus not included in a lot of compilers.

    strcpy() is standard and included in all compilers.
    May be you had strlcpy() in mind : size_t strlcpy( char *dst, const char *src, size_t siz);
    This function is an attempt of the BSD people to “fix” strncpy but, in many ways, strlcpy() is worse than strcpy().

    This lesson was first published with 'strlcpy()', and then was "corrected" to use "strcpy" instead.

  • salah

    Hi, Here I got the right result.. the output was: Copy this!
    is that still consider an error ?

    • nascardriver

      Accessing memory beyond an object invokes undefined behavior. You might get the expected output, you might get garbage, your program might crash, anything can happen.

  • Potedeo

    Because not all compilers support strcpy_s(), strlcpy() is a popular alternative.
    What a nice new std function :)
    I'm pretty sure you wanted to say "strcpy_s(), strcpy()"

  • Ged

    I have a program that I need to make. It looks for a specific character in a long text and prints how many times it was used. I can check if the program is OK online on the website ( it uses a C++11 / C++14 compiler). The programs works, but I get another problem "Time Limit Exceeded". It takes about a second for the program to run and I need it to be faster. Do you have any idea what could help out? Thanks in advance.

    • nascardriver

      Formatted extraction via `operator>>` and `std::strlen` are slow.
      Your code is unsafe, as `word` will overflow if a word is longer than 20 characters.

      `operator>>` can be replaced with `read()`, which doesn't do any formatting.
      `std::strlen` is unnecessary, you know how long your buffer is.

      You can play around with the buffer size. Larger buffers should be faster. This code should be at least twice as fast as yours.

  • Wallace

    A few minor typos:

    "An" should be "A":

    "compiler" should be "compilers":
    "Because not all compiler support strcpy_s()"

    "it's" should be "its":
    "It also has it’s own set of issues."

    Thanks for a great resource!

  • alfonso

    "Because not all compiler support strcpy_s()..."

    I do not have strcpy_s (). I thought standard libraries are the same. But it looks like someone add new functionality to a standard library and someone else do not.

    • The C++ standard defines some functions and types that are optional. Implementations with and without those entities are both valid. To guarantee that your code works everywhere, avoid those entities altogether unless you provide alternatives.

      • alfonso

        To be even more confusing ...
        I have Arch Linux on 64 bit, CodeBlocks (using clang or gcc).
        If I use CodeBlocks, I can use std::size() (from <iterator> header) with both clang and gcc.
        If I use the terminal with clang++ or g++, std::size() (from <iterator> header) is not found or something like that.
        But CodeBlocks use the same clang (or gcc) that is installed on Arch Linux... 0_O

  • alfonso

    Why is sizeof () always the same? On my system 32.

    sizeof (<some_structure_identifier_here>) gives me the size of that structure in bytes, but this is not the case with std::string objects.

    • `std::string` stores a pointer to the char array, it doesn't store the string directly. You'll understand it by the end of this chapter.

      • alfonso

        This is somehow unexpected for a beginner, because sizeof (arr) (where arr is a fixed array) gives the whole size of the array. Even if arr decays to a pointer. Maybe because it would be almost useless to give the size of a pointer, and more valuable to give the whole size of the array. But then sizeof (std::string) being always the same, it looks like it is also useless. Or maybe don't. So many little details in C++. :)

        • > Even if arr decays to a pointer
          No. `sizeof(arr)` returns the size of the pointer if the array decayed.

          `sizeof` is supposed to give you the size of the data type, and that's what it's doing. If you want to know something type-specific, eg. string length, you should use the functions provided by that type (std::string::length).
          You only need `sizeof` if you're doing operations on the raw data. Use `std::size` for arrays.

  • alfonso

    Is this better? Here strlen () is not called and executed for every iteration.

  • Benur21

    Instead of 'strcpy()', why not just 'source = dest'?

  • BP

    I have a quick question. Say I was writing code to play a game of hangman.
    I'm guessing for that game, I will need to compare every single letter of a word.
    How would I do this using std::string?


    • Use a for-loop. Since you didn't post any code, this is what I suppose you're stuck at

      I don't want to spoil you, so I'm not posting the rest of the code. If you have another question, feel free to ask. If you just want to see the start of 1 possible implementation without trying yourself, have a look here

      • BP

        Hi thanks for responding!

        I am not really coding that game, it was just an example in my mind.
        I was wondering how to look at the single letters in a string without c-style strings.
        And to be honest, I don't get most of your example.

        I'm guessing std::string::size_type is a special type of string?
        and if strSolution is the word in std::string type then what is strSolution.length()? is it just the length of that word?
        Are you jumping letter with ui?
        Is this material explained later in this tutorial?


        • Chapter 17 is all about `std::string`. (Watch out, it's an old chapter with bad practices).

          `std::string::size_type` is the type used by `std::string` for indexing. It's some unsigned integer type.

          `std::string::length` (same as `std::string::size`) returns the length of the string.
          "Hello" -> 5
          "BP" -> 2

          ui starts at 0 and goes all the way up to the string's length minus 1. For example, given the string "Hello", ui goes 0, 1, 2, 3, 4. (5 is an invalid index).

          `std::string`'s individual characters can be accessed just like those of a C-style string, using `str[index]`.

          • BP

            So since I still need to learn lot's or stuff before chapter 17, I thought I would just for the fun of it try using C-style strings for hangman.

            It is a bit ugly, and spaghetti like, but here is my attempt.
            I hope it is a bit ok...

            Thanks for all your help!

            • The only place where you used a C-style string is line 63, which is only required for the fixed-size array. Other than that your code is already using `std::string`.
              You should be able to understand chapter 17, I don't know why Alex decided to wait until the end.

              Some suggestions:

              - Limit your lines to 80 characters in length for better readability on small displays.
              - Line 19+: I don't think char extraction can fail.
              - Line 69: That `false` doesn't do anything. The entire array is initialized to `false` by using empty curly brackets.
              - Line 98 has no effect. `lettersGuessed` isn't used after here.
              - You could remove the "word"/"letter" test by always reading a string and checking its length. If it's 1, you run the letter code. If it's > 1, you run the word code.

              • BP

                How would I check the length of a array if it's size should be fixed?
                It doesn't matter what I put in the machine the size is fixed.
                I guess this can be solved by using non fixed arrays?
                I'll probably see those in a few chapters.
                Thanks again for your pointers!

              • Alex

                The core lessons of this tutorial are focused on broad understanding. Originally, the lessons at the end were going to be a deeper dive into some of the more common classes of the standard library -- but I didn't get far in that approach.


    #include <iostream>
    int main()
        char name[2]; // declare array large enough to hold 255 characters
        std::cout << "Enter your name: ";//mangesh
        std::cin.getline(name, 3);
        std::cout << "You entered: " << name << '\n';

        return 0;

  • Stani

    Thank you so much for this wonderful tutorial! I finally feel I can learn c++!
    My question is the following: I wanted to change the value of one element of the string as:
    char ss[] = "feast";
    ss[0] = "b";
    std::cout << ss;

    which gives me the error: invalid conversion from 'const char*' to 'char' [-fpermissive]|

    but when I switch it to:
    ss[0] = 'b';

    everything compiles fine. Why?

  • Alireza

    Hello and good morning,

    why does an array outputs its address when we're using array(itself, without braces), and @string which is C-style string does not ?!
    Skim below to understand.

  • Dimbo1911

    I have a question. How come when you overflow the array (the example with strcpy()), it still prints out the array as it should (I can print out the string in the dest same as in the source)? The length of the dest (strlen) after copying is equal to the actual length of the dest + the length of the string that was copied, but the sizeof dest still returns declared length of the string. Is this dependent on the machine and/or the compiler? (Windows 7x64 and Code::Blocks 17.12) Does the compiler allocate the array dynamically? Also, what happens with the strlen, why is it the original length + lenght of the copied string, not just the length of the copied string OR the original length? Thank you for your time

    • @std::strcpy doesn't check bounds, it copies as much as you tell it to. If you tell it to write to memory you're not supposed to access, behavior is undefined.
      @sizeof returns the length you declared the array with, it can't change at run-time.
      @std::strlen loops through the string until it finds the 0-terminator. Again, without bounds checks, if it accesses memory after the reserved length, behavior is undefined.

      • Dimbo1911

        Oh, ok, so if I got it right, it is a matter of pure luck if the program will output what we gave it if we write out of bounds? Like, if some other program overwrites the memory we accesed by going out of bounds, and wrote over the /0 terminator, the program will keep outputing random garbage, untill it, by chance, finds first /0 terminator?
        Thanks for the answer :)

        • > some other program overwrites the memory we accesed
          That can't happen, at least not on the major OSs. But other variables or code in your program could conflict with the memory you're trying to access.
          @std::cout prints everything until it finds the 0-terminator, or your program crashes, because it's accessing memory which it's not allowed to access, whichever comes first.

  • oliver

    When I try to convert std::size(array) into an integer type, vb gives me an error and says that in order to convert from 'size_t' to' int' I need to do a narrowing conversion. However in your code you did not have to do a narrowing conversion. I changed my c++ version to c++17, and also tried changing it to the latest update, but the error message still came up. Could anyone please explain this to me? Thanks

    • Use a @static_cast.
      Narrowing conversions usually cause a warning, you probably set your compiler up to treat warnings as errors (This is a good thing).

  • TheBeginer

    Hello, can you please help me with this code:

    The problem is, when I read the string from the file, it repeats a character at the end.
    e.g. If I store "welcome" in the file, it prints "welcomee" on the screen although the file contains the string "welcome".

    • * Line 8, 9, 12, 22: Initialize your variables with brace initializers.
      * Line 23: Booleans are false/true.
      * Line 14-19: Should be a for-loop.
      * Don't use "using namespace".
      * Missing printed trailing line feed.
      * Line 18: Use ++prefix unless you need postfix++.
      * Magic number: 80. Use @std::size.

      You're not writing a null-terminator to the file.

  • What I wonder now is this... Is there a way to convert a C-string into a std::string and vice versa.... Like this pseudo code:

    I'm not sure if I would ever actually need this, but just in case ;)

  • Alamin Sheikh

    why its important ??

  • The first line only supports C style string variable as its first argument.
    The second line only supports string variable as its second argument.
    Are the getline functions same in both lines.
    I am confused with it.

Leave a Comment

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