Navigation



6.6 — C-style strings

Under regular C (and hence also C++), it is possible to use arrays to represent strings. A string is a sequence of chars that are interpreted as a piece of text. You have already seen string literals:

cout << "This is a string literal";

In C and C++, strings are typically represented as char arrays that have a null terminator. A null terminator means that the string ends with a ‘\0′ character (which has ASCII code 0). Arrays that are null terminated in this manner are often named using the Hungarian Notation prefix “sz”.

To declare a C-style string, simply declare a char array and assign it a value:

char szString[] = "string";

Although “string” is only 6 letters, this actually declares an array of length 7. The following program prints out the length of the string, and then the ASCII values of all of the characters:

cout << sizeof(szString) << endl;
for (int nChar = 0; nChar < sizeof(szString); nChar++)
    cout << static_cast<int>(szString[nChar]) << " ";

This produces the result:

7
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.

Just like with normal arrays, once an array is declared to be a particular size, it can not be changed. Our szString above is of length 7 — which means it can fit 6 chars of our choice and the null terminator. If you try to stick more than 6 chars in the array, you will overwrite the null terminator and the CPU won’t know where the string ends. If you try to print a string with no null terminator, you’ll not only get the string, you’ll also get everything in the adjacent memory slots until you happen to hit a 0.

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

It is important to realize that a single char (eg. ‘a’) is typically only allocated one byte, but the equivalent string (eg. “a”) is allocated two bytes — one for the char, and one for the null terminator.

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

char szString[] = "string";
szString[1] = 'p';
cout << szString;

This snippet prints:

spring

One important point to note is that 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!

char szString[] = "string"; // ok
szString = "rope"; // not ok!

This would be the conceptual equivalent of the following nonsensical example:

int anArray[] = { 3, 5, 7, 9 };
anArray = 8; // what does this mean?

Buffers and buffer overflow

You can read text into a string using cin:

char szString[255];
cin >> szString;
cout << "You entered: " << szString << endl;

Why did we declare the string to be 255 characters long? The answer is that we don’t know how many characters the user is going to enter. We are using this array of 255 characters as a buffer. A buffer is memory set aside temporarily to hold data. In this case, we’re temporarily holding the user input before we write it out using cout.

If the user were to enter more characters than our array could hold, we would get a buffer overflow. A buffer overflow occurs when the program tries to store more data in a buffer than the buffer can hold. Buffer overflow results in other memory being overwritten, which usually causes a program crash, but can cause any number of other issues. By making our buffer 255 charaters long, we are guessing that the user will not enter this many characters. Although this is commonly seen in C/C++ programming, it is poor programming.

The recommended way of reading strings using cin is as follows:

char szString[255];
cin.getline(szString, 255);
cout << "You entered: " << szString << endl;

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

Manipulating C-style strings

C++ provides many functions to manipulate C-style strings. For example, strcpy() allows you to make a copy of a string.

char szSource[] = "Copy this!";
char szDest[50];
strcpy(szDest, szSource);
cout << szDest; // prints "Copy this!"

However, strcpy() can cause buffer overflows! In the following program, szDest isn’t big enough to hold the entire string, so buffer overflow results.

char szSource[] = "Copy this!";
char szDest[4];
strcpy(szDest, szSource); // buffer overflow!
cout << szDest;

It is better to use strncpy(), which takes a length parameter to prevent buffer overflow:

char szSource[] = "Copy this!";
char szDest[50];
strncpy(szDest, szSource, 49); // copy at most 49 characters (indices 0-48)
szDest[49] = 0; // ensures the last character is a null terminator
cout << szDest; // prints "Copy this!"

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)
strlen() — Returns the length of a string (excluding the null terminator)

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

// Ask the user to enter a string
char szBuffer[255];
cout << "Enter a string: ";
cin.getline(szBuffer, 255);

int nSpacesFound = 0;
// Loop through all of the characters the user entered
for (int nChar = 0; nChar < strlen(szBuffer); nChar++)
{
    // If the current character is a space, count it
    if (szBuffer[nChar] == ' ')
        nSpacesFound++;
}

cout << "You typed " << nSpacesFound << " spaces!" << endl;

std::string

It is important to know about C-style strings because they are used in a lot of code. However, we recommend avoiding them altogether whenever possible!

A better idea is to use the string class in the standard library (std::string), which lives in the string header. std::string lets you work with strings in a way that is much more intuitive. You can assign strings to them using the assignment operator and they will automatically resize to be as large or small as needed.

Here is a quick example using std::string:

#include <string> // for std::string
#include <iostream>

int main()
{
    using namespace std; // for both cout and string
    cout << "Enter your name: ";
    string strString;
    cin >> strString;
    cout << "Hello, " << strString << "!" << endl;

    cout << "Your name has: " << strString.length() <<
            " characters in it" << endl;
    cout << "The 2nd character is: " << strString[1] << endl;

    strString = "Dave";
    cout << "Your name is now " << strString << endl;
    cout << "Goodbye, " << strString << endl;

    return 0;
}

One extremely useful function to use with std::string is getline(). This allows you to read an entire string in, even if it includes whitespace:

cout << "Enter your full name: ";

string strName;
getline(cin, strName);

cout << "You entered: "<< strName <<endl;

For example:

Enter your full name: John Smith
You entered: John Smith

The nice thing about std::string is that you don’t have to guess how large the input string is likely to be in advance!

We will talk more about std::string in future lessons. But feel free to experiment with it in the meantime.

6.7 — Introduction to pointers
Index
6.5 — Multidimensional Arrays

50 comments to 6.6 — C-style strings

  • sandor

    Hello, I’ve got a question about the cin.getline() function.
    For example:
    char string[10];
    cin.getline(string, 10);
    cout
    If I know enter more than 10 chars the program just runs through and the first entered 10 chars doesnt get shown.How can I force the program to show the first 10 chars?

  • In your example, getline() should only show the first 9 characters you enter (the 10th is used for the terminator) and ignore the rest.

    For example, if I run this program:

    char string[10];
    cin.getline(string, 10);
    cout < < string << endl;
    

    and I type 123456789abcdefghijk as input, the program outputs:

    123456789
    

    If you actually want to show 10 chars instead of 9, you'll have to change the size of the buffer to 11:

    char string[11];
    cin.getline(string, 11);
    cout < < string << endl;
    
  • sandor

    Sorry, that was a off-by-one thinking mistake of me : ) But it doesnt really matter how many chars are shown. Its just that when I run this program, and enter more than the “maximum” of chars, it doesnt show them, but instead runs through, terminating immediatly (maybe it shows them quick, but if, then only for a millisecond). My actual question is now, how can I make the window stay open and show me the enterd chars.
    regards

  • Sandor, I believe what’s happening is that all of the keys you are entering are going into an I/O buffer, and the code you wrote is only reading 10 of them. When a program terminates, some compilers hold the window open until is key is pressed. It sounds like your compiler is doing this, but then a key from the I/O buffer is causing it to close immediately.

    I am not sure if this would work, but at the end of your program (right before main returns), try adding this:

    std::cin.ignore(1000, '\n');
    
  • Cody

    Shouldn’t

    char szSource[] = "Copy this!";
    char szDest[50];
    strncpy(szDest, szSource, 50); // copy at most 50 characters
    cout < < szDest; // prints "Copy this!"

    copy at most 49 characters to prevent the null terminator from being overwritten.

  • Cody,

    In most cases, it is a good idea to copy 1 less than the number of characters in the buffer (in this case, 49) and then explicitly ensure the last character is a null terminator. I have updated the example.

  • Abhishek

    char szSource[] = “Copy this!”;
    char szDest[50];
    strncpy(szDest, szSource, 49); // copy at most 49 characters (indices 0-48)
    szDest[49] = 0; // ensures the last character is a null terminator
    cout

    In this example you have assigned 0(zero) to a char.Will the compiler automatically cast 0 to ‘\0′?

    How about writing szDest[49]=’\0′;

    or szDest[49]=static_cast(0)

    Does cin.getline() place the null character automatically?

    char szString[255];
    cin.getline(szString, 255);
    cout

    • Yes, the compiler should treat 0 and ‘\0′ as the same.

      So:
      szDest[49] = 0;
      szDest[49] = ‘\0′;
      szDest[49] = static_cast(0);

      All should work identically.

      cin.getline() reserves one space for a null terminator, so it does place it automatically.

  • Chad

    that program will only read in my first name. How do you accept strings with spaces in them?

  • Stuart

    My compiler says that “strncpy” may be unsafe and that to consider using “strncpy_s” instead.
    What’s the difference?

    • strncpy is a standard function call that copies n bytes of a string from source to destination. Microsoft decided that this function was not safe enough, so they deprecated it in the latest version of their compiler, and replaced it with strncpy_s. The two functions are identical, except strncpy_s takes an additional parameter that is the size of the destination buffer. strncpy_s is currently a non-standard function and may not be portable.

      Personally, I’d avoid both and use a string class or std::string.

  • Jeffey

    in the code

    string strString;

    it is setting string to a variable right? How come string is not considered a “keyword”? Like instead of asking the user. I could do the following.

    string strString;
    strString = ("jeffey");

    this work the same as all other variables.

    also is there a way I can set my compiler settings to recognize the word “string” so that it shows up as a different color. I do use the text colors and rely on them a lot when looking at code. I want the word “string” to stand out from normal text.

  • string strString; is simply declaring a variable of type std::string. If I’m not mistaken, std::string will auto-initialize to the empty string if not provided a value. string isn’t a keyword because it wasn’t built into the language — it’s part of the standard library.

    As far as I know, there isn’t a way to make string show up in a different color in Visual Studio or Code::Blocks.

    • jeremy

      In Code::Blocks,
      Go To “Settings” > “Editor” > “Syntax highlighting” > “Keywords…”
      Add “string” to the list.
      It will now appear as a keyword like “int”.

  • som shekhar

    if i use like this :
    string sName;
    then this variable doesnt take blank spaces;

    for ex;if i enter a som shekhar in the variable sName, then it prints only som
    y it is like that?

    • It has to do with the way the >> operator is implemented. Operator >> will extract characters until it hits whitespace, then it will stop.

      The reasons for this are somewhat complex — it is discussed further in chapter 13.2 on the lesson on the >> operator.

      In the meantime, if you want to read in a string with whitespaces, you can do this:

      string strName;
      getline(cin, strName);
      cout << strName << endl;
      
      • som shekhar

        now i have done the same but hte problem is that i have to hit the enter key twice…
        my code is

        #include <iostream>
        
        #include <string>
        
        int main()
        {
        using namespace std;
        string strName;
        cout<<"Enter name:";
        getline(cin,strName);
        cout<<"Name = "<< strName << endl;
        return 0;
        }
        
  • When you use cin.getline, you make the string a maximum amount of 255 – surely there’s a huge chance of so much wasted memory? Is there no way to prevent this?

    • The memory isn’t really wasted, it’s just used temporarily and then gets returned to the stack when the function exits. The bigger problem is the possibility of overflowing the buffer.

      If you want to avoid using the fixed size buffer, you can use the getline() function with a std::string.

      std::string strString;
      getline(cin, strString);
      
  • Hey, I have a quick question… Let’s say we have a function Fred

    std::string Fred();

    Now I need to make a function Barney

    char *Barney()

    Which calls Fred and returns the result as an null-terminated-c-string. How would I go about this?

  • csvan

    In the past, I have been giving thought to the question of resource overheads when using std::string as opposed to cstrings. Just how big is this overhead in reality, and could it be preferable to use cstrings rather than std::string in code where there will be no “heavy” use of strings, in order to improve overall performance? Maybe the same goes for std::vector as opposed to arrays?

    I

  • Frank

    I added to your example program just for giggles and i thought i would share my minor extension. by the way alex this is a great tutorial and i am learning a lot but i am still struggling a bit with writing a coherent program. perhaps if there were a few example programs (3-4 with explainations of what each part does ect.) at the end of each chapter just for us to copy? this would help me out tremendously to grasp what you are telling us in the sections.

    here is the code:

     #include <cstdlib>
    #include <iostream>
    #include <string>
    
    int main()
    {
        using namespace std;
              cout << "Enter your name: ";
              string strString;
              cin >> strString;
              cout << "Hello, " << strString << "!" << endl;
              cout << "Your name has " <<strString.length() <<
              " characters in it" << endl;
              cout << "The 2nd character is: " << strString[1] << endl;
              strString = "Dave";
              cout << "your name is now " << strString << endl;
              cout << "Hello " << strString << ", Would you like to hear me sing a song?" <<endl;
              string strAnswer;
              cin >> strAnswer;
                  if ( strAnswer == "yes" )
                     cout << " \n Daisy, Daisy give me your answer do. \n Im half crazy all for the love of you \n \n";
                     else if (strAnswer == "no")
                          cout << "Ok, Goodbye " << strString << endl;
                          else
                          cout << "I dont understand you " << strString << endl;
    system ("PAUSE");
    
    return 0;
    
  • [...] a previous lesson, we covered C-style strings, which using char arrays to store a string of characters. If you’ve tried to do anything with [...]

  • Allan

    How about something on working with strings?
    I need to add two strings together to create one longer string.

    Also when comparing strings can I just ask if string1==string2 or is there a strcompare function?

    I’m a newbie writing my first program and referencing your site.

    Thanks,
    Al

  • Rob
    char szString[255];
    cin.getline(szString, 255);
    cout << "You entered: " << szString << endl;
    

    Won’t this example over write the null terminator?

  • smitha

    This is a question regarding passing strings as parameters. When I pass a string constant as a parameter to a function and if I do a gets on the same variable, should it not overwrite the string? An older version of Turbo C++ does not give an error but DevC++ shows a runtime error during gets()
    for example

    #include <iostream.h>
    #include <stdio.h>
    #include <string.h>
    void Display (char *str)
    {
       gets(str);
       puts(str);
    
    }
    int main ()
    {
    	Display("xyzxyz");
    	system("pause");
    }
    

    assuming that the user enters abcabc, shouldnt str have the value abcabc, instead it runs into memory problems…though turboc++ works just fine.

  • DaBlackIce

    I’ve had this problem recently…

    Every time I use a do while loop and I use eg.:

     string strWhateverString;
    getline(cin, strWhateverString);

    , the compiler doesn’t wait for any input and skips to the next statements. What would be the problem? If you already answered this question I’m sorry…thank you in advance :)! BTW GREAT TUTORIAL…IT HAS BEEN A LOT OF HELP!

  • DaBlackIce

    I kind of found out what was wrong. Apparently it’s not the do while loop.

    #include "stdafx.h"
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    struct nPersonalInfo
    {
    	string strFullName;
    	unsigned short nAge;
    	string strBirthday;
    	float fHeight;
    	unsigned short nWeight;
    };
    
    void  PrintInfo(string strFullName, unsigned short nAge, string strBirthday,
    	float fHeight, unsigned short nWeight)
    {
    	cout << strFullName << "'s information."<< endl;
    	cout << endl;
    
    	cout << "Full Name:t" << strFullName << endl;
    	cout << "Age:tt" << nAge << endl;
    	cout << "Birthday:t" << strBirthday << endl;
    	cout << "Height:tt" << fHeight << " meters tall" << endl;
    	cout << "Weight:tt" << nWeight << " lb" << endl;
    	cout << endl;
    }
    
    void AskUserInfo()
    {
    	cout << "Input your personal information." << endl;
    	cout << endl;
    
    	nPersonalInfo sMember;
    
    	cout << "What's your full name? ";
    	getline(cin,sMember.strFullName);
    
    	cout << "How old are you? ";
    	cin >> sMember.nAge;
    
    	cout << "When were you born? (mm/dd/yy)" << endl;
    	cin>> sMember.strBirthday;
    
    	cout << "What is your height in meters? ";
    	cin >> sMember.fHeight;
    
    	cout << "What is your weight in pounds? ";
    	cin >> sMember.nWeight;
    	cout << endl;
    
    	PrintInfo(sMember.strFullName, sMember.nAge, sMember.strBirthday,
    		sMember.fHeight, sMember.nWeight);
    
    	cout << endl;
    }
    
    int main ()
    {
    	cout << "How many people are in your family? ";
    	int nNumberOfFamilyMembers;
    	cin >> nNumberOfFamilyMembers;
    	cout << "You have " << nNumberOfFamilyMembers << " family members." << endl;
    
    	AskUserInfo();
    	for (int iii = 0; iii < nNumberOfFamilyMembers; iii++)
    		//AskFamilyInfo(); I won't include this function to make it short...
    	return 0;
    }
    

    If you run this program after putting in the amount of family members, it skips the question “What is your full name?”

    But if I change:

    cin >> nNumberOfFamilyMembers; 

    to:

    getline(cin, nNumberOfFamilyMembers);

    and use nNumberOfFamilyMembers as a string instead, everything works fine. But still….I wanted to do it this way. And even so, in the loop I have to change

    nNumberOfFamilyMembers

    to

    nNumberOfFamilyMembers.length

    since it would be a string now.

    Anything I can do about it?

  • DaBlackIce

    Oh by the way..in the PrintInfo function, beside full name, age, etc. it should be t for tab…not t only and nNumberOfFamilyMembers.length() in the main function :)

  • i want a source code for strrev(),strlwr();

  •  i want a source code for strrev(),strlwr(); 
  • Ogre

    When you first use cin.getline() you say that the parameter 255 means it will only read the first 254 characters, to leave space for the null terminator. But if you initialize the array with [255] then it will have room for 255 characters plus the null terminator, right?

    Also, when you first use strncpy() you say that the parameter of 49 means that is the most amount of characters it will take (excluding the null terminator), but this is inconsistent with cin.getline(). Is this really how it is?

    You may also want to make it clear that we need to include for some functions.

  • newUser

    /* Section: header */
    #include <iostream>
    #include <fstream>
    #include <cstdlib>
    #include <string>

    /* Section: namespace */
    using namespace std;

    /* Section: constant */
    const string NEWLINE = "\n" // similar to: #define NEWLINE '\n'

    /* Section: prototype */
    void nfound( const string &"v_file" );

    int main( int argc, char *argv[] ) {
    string the_file = "test_file.txt";
    if ( "the_file" ) {
    cout << "File found!";
    } else {
    cout << "File not found!";
    nfound( "the_file" );
    }

    cin.get();
    }

    void nfound( const string &"v_file" ) {
    ofstream a_file ( "v_file" );
    a_file << "This text will go inside!NEWLINE";
    a_file.close();
    }

    This is my simple program that I want to check if a file exists, if not make it with some writting in it. The problem is I get these errors:

    14: error: expected ‘,’ or ‘;’ before ‘void’
    In function ‘int main(int, char**)’:
    22: error: ‘nfound’ was not declared in this scope
    At global scope:
    28: error: expected ‘,’ or ‘…’ before string constant

    I’ve looked over the code and it seems right. None of the tutorials I’ve read (best by far! really love the code section you have!) deal with a string as a parameter. Last tutorial didn’t mention the string header (#include ), but including this still didn’t help.

    • newUser

      I’ve made some minor adjustions, namely how I did namespaces (used the suggestion from the section) and I corrected the function parameter constant.

      /* Section: header */
      #include <iostream>
      #include <fstream>
      #include <cstdlib>
      #include <string>

      /* Section: constant */
      const std::string NEWLINE = "\n"

      /* Section: prototype */
      void nfound( const std::string &v_file );

      int main( int argc, char *argv[] ) {
      using namespace std;
      string the_file = "test_file.txt";
      if ( "the_file" ) {
      cout << "File found!";
      } else {
      cout << "File not found!";
      nfound( "the_file" );
      }

      cin.get();
      }

      void nfound( const std::string &v_file ) {
      using namespace std;
      ofstream a_file ( "v_file" );
      a_file << "This text will go inside!NEWLINE";
      a_file.close();
      }

  • Iodream

    Hello. What if i want to access each symbol of a string array?
    Would it look something like this:

    
    string  szStringArray[3];
    
    for (int iii=0;iii<3;iii++)
     for (int jjj=0;jjj<szStringArrayAdd[iii].length();jjj++)
       szStringArrayAdd[jjj][iii]= // do something. 
    

    ?

    • I’m not sure what you tried to do there, but the code looks really wrong.
      wouldn’t a single for loop suffice?

      • Iodream

        Well, i initialize an array of 3 strings.
        Then the first loop goes through the indexes of the array and
        the 2nd loop through each character of the currently selected by the first loop string.
        Yes, i made a mistake when i initialize the array there should be a different name “szStringArrayAdd[3]”
        Im just trying to write a program and im getting errors (errors directly in Visual Studio’s code window)
        I am asking if what ive written above is legal.

      • Iodream

        Well, i initialize an array of 3 strings.
        Then the first loop goes through the indexes of the array and
        the 2nd loop through each character of the currently selected by the first loop string.
        Yes, i made a mistake when i initialize the array there should be a different name “szStringArrayAdd[3]”
        Im just trying to write a program and im getting errors (errors directly in Visual Studio’s code window)

        I am asking if what ive written above is legal.

  • emailaddress1012

    I get something curious when I do the following:

    char *szString = “string”;

    cout << sizeof(szString) << endl;
    for (int nChar = 0; nChar < sizeof(szString); nChar++)
    cout << static_cast(szString[nChar]) << " ";

    Result:
    4
    115 116 114 105

    But when I declare char szString[] = "string" as is done in the tutorial, I get the expected result:

    7
    115 116 114 105 110 103 0

    Does anyone know why this is? I thought these were equivalent ways of declaring C-style strings.

    • pravin_ms

      This is because , you have declared szString as pointer.
      Pointer is a variable that holds address of another variable. The address is a number i.e. nothing but a kind of integer.

      So using sizeof(szStrings) is similar to using sizeof(int) which returns the size of int in bytes.

      To confirm this run the following code :
      void main()
      {
      int a=20;
      cout<<sizeof(a);
      return ;
      }
      It should display value 4 i.e. 4 bytes.

  • MrFinn

    For those outside the USA, note that you will want to use 2 byte unicode characters instead of ASCII (1 byte) character set which is a small subset of Unicode. Most of the stuff in this chapter apply only to ASCII and commands for unicode character manipulation take a different form.

  • abhi4556

    what is wrong in this code
    its giving run time error
    int main()
    {
    int y=4;
    int *x=new int;
    x=&y;
    cout<<"val of y::"<<*x;
    delete x;
    return 0;
    }

You must be logged in to post a comment.