Strings
A string is a sequence of characters — C++ gives you two ways to work with them, the low-level C-style character array and the safer, self-managing std::string.
A string represents a sequence of characters. C++ supports strings in several forms, but two major forms are: C-style strings — character arrays terminated by '\0' — and std::string — the standard C++ string class. Modern C++ code generally prefers std::string for ordinary string handling because it manages its own storage and provides many useful operations.
What Is a String?
A string is a sequence of characters. It can contain letters, digits, spaces, symbols, and punctuation.
The value contains a sequence of characters: S a m p l e A p p L a b.
Another example of a string value made up of letters, punctuation, and a space.
std::string name = "SampleApp Lab";std::string message = "Hello, C++!";C++ Does Not Have a Built-In string Keyword
Unlike some languages, string is not a built-in fundamental type in C++. Writing string name; requires string to be available through a namespace or alias. The standard C++ string type is std::string, provided by #include <string>.
Including <string> and using the std:: prefix (or a using declaration) is what makes std::string available — there is no plain "string" keyword built into the language.
#include <iostream>
#include <string>
int main()
{
std::string name = "Rina";
std::cout << name << '\n';
return 0;
}String Literals
A string literal is text enclosed in double quotes: "Hello". Examples include "SampleApp Lab", "Automotive", "C++", and "12345". A string literal is different from a character literal.
'A' is one character; "A" is a string containing that one character.
The first represents a single character. The second represents a sequence containing that character.
Character: 'A'
String: "A"std::string
std::string is the standard C++ string type. It represents a sequence of characters and manages its own string storage.
Declares and initializes a std::string.
You can change the string:
Assigning a new value replaces the string's contents.
You can also create an empty string:
A default-constructed std::string starts out empty.
#include <string>
std::string name = "SampleApp Lab";name = "Embedded Systems";std::string text;Initializing Strings
std::string supports several initialization styles.
Copy, direct, and list initialization all produce an equivalent string; omitting an initializer (or using {}) produces an empty string.
std::string name = "Rina"; // copy initialization
std::string name("Rina"); // direct initialization
std::string name{"Rina"}; // list initialization
std::string name; // empty string
std::string name{}; // empty stringPrinting a String
std::string works directly with std::cout via operator<<.
Create the string "SampleApp Lab".
Stream it to std::cout.
Output: SampleApp Lab
#include <iostream>
#include <string>
int main()
{
std::string name = "SampleApp Lab";
std::cout << name << '\n';
return 0;
}Reading a String with std::cin
Formatted extraction reads a string from standard input.
For input "Rina" the string becomes "Rina". However, formatted extraction stops at whitespace — if the user enters "Rina Sahu", std::cin >> name; reads only "Rina".
std::string name;
std::cin >> name;Reading a Complete Line
Use std::getline() when the input can contain spaces.
For input "Rina Sahu" the entire line is stored: "Rina Sahu".
A complete program that reads and echoes a full line, spaces included.
std::string name;
std::getline(std::cin, name);#include <iostream>
#include <string>
int main()
{
std::string name;
std::cout << "Enter your name: ";
std::getline(std::cin, name);
std::cout << "Hello, " << name << '\n';
return 0;
}std::cin and std::getline() Together
A common problem occurs when formatted extraction is followed by getline().
Suppose the input is "25" then "Rina Sahu". After reading 25, the newline remains in the input stream, and getline() may immediately consume that remaining newline instead of the name.
A common solution is to explicitly discard the leftover newline before calling getline().
std::cin.ignore() discards characters up to and including the next newline, clearing the way for getline() to read the intended line.
int age;
std::string name;
std::cin >> age;
std::getline(std::cin, name);#include <iostream>
#include <string>
#include <limits>
int age;
std::string name;
std::cin >> age;
std::cin.ignore(
std::numeric_limits<std::streamsize>::max(),
'\n'
);
std::getline(std::cin, name);String Length
std::string provides length() and size().
Both calls print 5. For std::string, size() and length() return the same value.
std::string text = "Hello";
std::cout << text.length() << '\n';
std::cout << text.size() << '\n';Accessing Individual Characters
Output: H, e, o. Indexing is zero-based.
Each character occupies one zero-based index position.
std::string text = "Hello";
std::cout << text[0] << '\n';
std::cout << text[1] << '\n';
std::cout << text[4] << '\n';+---+---+---+---+---+
| H | e | l | l | o |
+---+---+---+---+---+
0 1 2 3 4Modifying Individual Characters
A non-const std::string can be modified through operator[].
Output: Jello. The string itself remains the same string object, but one of its characters has changed.
std::string text = "Hello";
text[0] = 'J';
std::cout << text;at() for Bounds-Checked Access
Unlike operator[], at() performs bounds checking and throws an exception when the index is outside the valid range — text.at(10); for "Hello" is invalid.
std::string text = "Hello";
std::cout << text.at(1);[] vs at()
A side-by-side comparison of operator[] and at().
Operation | text[index] | text.at(index)
Character access | Yes | Yes
Bounds checking | No | Yes
Invalid index | Undefined behavior | Throws exception
Typical use | Fast direct access | Checked accessConcatenating Strings
Strings can be joined using +.
Result: Hello World.
You can also use +=:
Now text = "Hello World".
std::string first = "Hello";
std::string second = "World";
std::string result = first + " " + second;std::string text = "Hello";
text += " World";String Comparison
std::string supports comparison operators.
Output: Equal. Other operators include ==, !=, <, >, <=, >=. Comparisons are based on the characters in the strings.
std::string a = "hello";
std::string b = "hello";
if (a == b)
{
std::cout << "Equal";
}Lexicographical Comparison
String comparison is generally lexicographical.
Output: a comes before b. The comparison proceeds according to the character values used by the string's character type.
Do not assume that human dictionary ordering for every language is identical to simple character-code comparison.
std::string a = "apple";
std::string b = "banana";
if (a < b)
{
std::cout << "a comes before b";
}Searching Within a String
If found, position contains the starting index. If "Lab" starts at index 10, then position == 10.
std::string text = "SampleApp Lab";
std::size_t position = text.find("Lab");Checking Whether find() Succeeded
find() returns std::string::npos when the requested text is not found.
This is the standard pattern for checking a search result.
std::string text = "SampleApp Lab";
if (text.find("Lab") != std::string::npos)
{
std::cout << "Found";
}Finding a Character
The result is the position of the first matching character.
std::string text = "SampleApp Lab";
std::size_t position = text.find('L');Finding the Last Occurrence
rfind() searches from the end and returns the position of the last matching occurrence.
std::string text = "one two one";
std::size_t position = text.rfind("one");Substrings
substr() extracts part of a string.
Result: SampleApp. The general form is text.substr(start, count).
For example, text.substr(10, 3); extracts three characters starting at index 10. If the second argument is omitted, text.substr(10); the substring continues to the end.
std::string text = "SampleApp Lab";
std::string part = text.substr(0, 9);Inserting Text
insert() can insert characters or strings.
Result: Hello C++ World. The insertion position determines where the new text is placed.
std::string text = "Hello World";
text.insert(6, "C++ ");Erasing Text
The result becomes Hello World. General form: text.erase(position, count);
std::string text = "Hello C++ World";
text.erase(6, 4);Replacing Text
Result: Hello C++. General form: text.replace(position, count, replacement);
std::string text = "Hello World";
text.replace(6, 5, "C++");Clearing a String
Afterward text = "".
You can check whether a string is empty using empty().
std::string text = "SampleApp Lab";
text.clear();if (text.empty())
{
std::cout << "String is empty";
}empty() vs size()
You can check whether a string contains characters using text.empty().
This is generally clearer than if (text.size() != 0) when the actual requirement is simply to check emptiness.
if (!text.empty())
{
std::cout << "String contains characters";
}Iterating Through a String
Using an index:
Output: H e l l o (each on its own line).
std::string text = "Hello";
for (std::size_t i = 0; i < text.size(); ++i)
{
std::cout << text[i] << '\n';
}Range-Based for
This processes each character directly.
For read-only processing, const can be used:
The loop variable cannot be modified, signaling that the loop only reads characters.
std::string text = "Hello";
for (char ch : text)
{
std::cout << ch << '\n';
}for (const char ch : text)
{
std::cout << ch << '\n';
}Modifying Characters During Iteration
A reference can be used when you want to modify the actual characters:
The result is Hello. The reference refers to the actual character stored in the string, so assigning through it changes the string.
std::string text = "hello";
for (char& ch : text)
{
if (ch == 'h')
{
ch = 'H';
}
}C-Style Strings
A C-style string is stored in a character array and ends with a null character '\0'.
Declares a character array holding "Hello" plus a trailing null terminator.
The '\0' marks the end of the string.
char text[] = "Hello";+---+---+---+---+---+-----+
| H | e | l | l | o | \0 |
+---+---+---+---+---+-----+String Literal and Null Terminator
When used to initialize a character array, the string literal "Hello" requires space for H e l l o \0. Therefore char text[] = "Hello"; has six characters in its array: 5 visible characters + 1 null terminator.
But std::string text = "Hello"; does not use the same C-style representation model for its interface; std::string manages its character sequence itself.
Character Array vs C-Style String
These are not necessarily the same concept.
This is a character array containing a valid C-style string — it ends with a null terminator.
This is a character array that does NOT contain a null-terminated C-style string.
The distinction is important when working with C-style string functions, which rely on the terminating '\0' to know where the string ends.
char text[] = {'H', 'e', 'l', 'l', 'o', '\0'};char text[] = {'H', 'e', 'l', 'l', 'o'};C-Style String Functions
C-style strings are commonly handled using facilities from #include <cstring>: std::strlen(), std::strcmp(), std::strcpy(), std::strcat(). These functions operate on null-terminated character sequences.
When using C++ for ordinary string manipulation, std::string is generally easier and safer to work with.
const char* text = "Hello";
std::cout << std::strlen(text);Converting Between C-Style Strings and std::string
A C-style string can be used to construct a std::string:
Constructing a std::string from a C-style string.
To obtain a pointer to a null-terminated character sequence from a std::string, use c_str():
c_str() is useful when interfacing with APIs that require a C-style string. The returned pointer refers to storage managed by the string and should not be treated as independently owned memory.
const char* text = "Hello";
std::string value = text;std::string text = "Hello";
const char* p = text.c_str();std::string::c_str()
The pointer returned by c_str() is intended for reading as a null-terminated character sequence.
Operations that modify the string can invalidate pointers or references to its character storage, so such pointers should not be assumed to remain valid indefinitely.
std::string message = "Hello";
const char* cText = message.c_str();
std::cout << cText;String Capacity
std::string maintains both size (number of characters currently stored) and capacity (amount of character storage currently available before another allocation may be required).
The exact capacity is implementation-dependent.
Do not assume capacity() == size().
std::string text = "Hello";
std::cout << text.size() << '\n';
std::cout << text.capacity() << '\n';Reserving Capacity
This requests enough capacity for at least 100 characters. It can be useful when you know that a string will grow significantly.
It does not mean that the string already contains 100 characters — after text.reserve(100); the string may still have size = 0 while having capacity for at least 100 characters.
std::string text;
text.reserve(100);Resizing a String
resize() changes the string's size.
You can also increase the size:
Additional characters are value-initialized.
You can explicitly provide a fill character:
Any newly added characters are set to '*' instead of the default.
std::string text = "Hello";
text.resize(3);text.resize(6);text.resize(8, '*');reserve() vs resize()
reserve() and resize() answer different questions.
reserve(100) only prepares storage; resize(100) actually changes the size to 100.
Operation | Meaning
reserve(n) | Requests capacity for at least n characters
resize(n) | Changes the number of characters to nstd::string text;
text.reserve(100); // does not make the string 100 characters long
text.resize(100); // makes its size 100Strings and Empty Values
An empty string contains zero characters:
Then text.empty() returns true, and text.size() returns 0.
std::string text;
// or
std::string text = "";Best Practices
Prefer std::string for normal C++ string handling — std::string name; instead of manually managing character arrays whenever a C-style interface is not required.
Use std::getline() for complete lines — std::getline(std::cin, text); and, when mixing formatted extraction with getline(), remember to consume the leftover newline first.
Use empty() when checking for an empty string — if (text.empty()) { /* ... */ }
Use size() or length() for string length, and text.capacity() only when you specifically need the currently allocated storage — the two answer different questions and are not interchangeable.
Use find() instead of manually searching when appropriate — if (text.find("C++") != std::string::npos) { /* Found */ }
Use const when a string should not be modified — const std::string text = "SampleApp Lab";
Access individual characters with valid indices, or use at() when you want bounds checking — indexing past the end is undefined behavior, the same as with a built-in array.
Keep '\0' (the null character, value zero) and '0' (the printable digit character) straight in your head — they are unrelated values that happen to look similar in text.
Don't hold onto a pointer obtained from c_str() across any operation that might modify or reallocate the string — re-fetch it afterward if you still need it.
Complete Example
A complete program combining line input, size(), empty(), indexing, and find() into one flow.
Prompt for and read a full line of input into name.
Print the name and its length.
If the name is not empty, print its first character.
If the name contains "Rina", print a confirmation message.
#include <iostream>
#include <string>
int main()
{
std::string name;
std::cout << "Enter your name: ";
std::getline(std::cin, name);
std::cout << "Name: " << name << '\n';
std::cout << "Length: " << name.size() << '\n';
if (!name.empty())
{
std::cout << "First character: " << name[0] << '\n';
}
if (name.find("Rina") != std::string::npos)
{
std::cout << "Name contains Rina\n";
}
return 0;
}C-Style String vs std::string
A side-by-side comparison of C-style strings and std::string across the operations covered in this topic.
Feature | C-style string | std::string
Representation | Character sequence ending in '\0' | C++ string class
Type | Usually char[] or const char* | std::string
Automatic size management | No | Yes
Concatenation | Manual/library functions | +, +=
Length | std::strlen() | size() / length()
Comparison | std::strcmp() | ==, <, etc.
Searching | C string functions/manual | find()
Substrings | Manual | substr()
Modification | Requires careful buffer management | Built-in operations
Common modern C++ choice | Mainly for C interoperability | YesQuick Reference
A one-page summary of the std::string operations covered in this topic.
What is the difference between the character literal 'A' and the string literal "A"?
'A' is a single character value; "A" is a string containing one character. They are different types with different sizes — a char array/string literal "A" implicitly includes a trailing '\0' when stored as a C-style string, while 'A' is just one char value.
Given std::string text = "Hello"; what is the difference between text[10] and text.at(10)?
text[10] is out of bounds and causes undefined behavior (operator[] does not check bounds). text.at(10) also refers to an invalid index, but at() performs bounds checking and throws an exception (std::out_of_range) instead of silently misbehaving.
Why can std::cin >> age; followed immediately by std::getline(std::cin, name); cause name to come out empty or wrong?
std::cin >> age; leaves the trailing newline character in the input stream after reading the number. The very next std::getline() call reads up to that leftover newline immediately, producing an empty (or truncated) line. The fix is to call std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); between the two reads to discard the leftover newline.
What does text.reserve(100) do, and how is it different from text.resize(100)?
text.reserve(100) only requests that the string have enough allocated storage for at least 100 characters; it does not change the string's size — text.size() can still be 0 afterward. text.resize(100) actually changes the number of characters in the string to 100, filling any new characters with a default (or specified) fill value.
What value does std::string::find() return when the search text is not present, and how should you check for that?
find() returns std::string::npos when the requested text is not found. The standard pattern is to compare the result against std::string::npos, e.g. if (text.find("Hub") != std::string::npos) { /* found */ }.
What does this program print?
SampleApp Lab text.substr(0, 9) extracts 9 characters starting at index 0, giving "SampleApp". text.substr(10) (no count argument) extracts from index 10 to the end of the string, giving "Lab".
What does this program print, and why does using char& instead of char matter here?
It prints heLLo. The range-based for loop uses char& (a reference), so each ch refers directly to the actual character stored inside the string — assigning to ch modifies the string itself. If the loop used a plain char instead, ch would be a copy of each character, and the assignments would have no effect on text.
Write a program that reads a full name (which may contain spaces) using std::getline(), then prints the name, its length, and whether it contains the substring "Hub".
The program uses std::getline(std::cin, name); to capture the entire line including spaces, then reports name.size() for the length and uses name.find("Hub") != std::string::npos to check whether "Hub" appears anywhere in the string.
Write a program that builds the C-style-string-safe character array {'H','i','!','\0'} character by character is NOT required — instead, write a program using std::string that starts empty, appends "Hi" and then "!" using +=, and prints both the final string and its size.
The program starts with an empty std::string, uses += to append "Hi" and then "!", producing "Hi!". It prints "Hi!" followed by its size, 3.
Operation | Code
Include | #include <string>
Create | std::string text;
Initialize | std::string text = "Hello";
Read word | std::cin >> text;
Read line | std::getline(std::cin, text);
Length | text.size()
Length | text.length()
Access | text[i]
Checked access | text.at(i)
Append | text += "abc";
Concatenate | a + b
Search | text.find("abc")
Last search | text.rfind("abc")
Substring | text.substr(pos, count)
Insert | text.insert(pos, value)
Erase | text.erase(pos, count)
Replace | text.replace(pos, count, value)
Empty check | text.empty()
Clear | text.clear()
Capacity | text.capacity()
Reserve | text.reserve(n)
Resize | text.resize(n)
C-style view | text.c_str()#include <iostream>
#include <string>
int main()
{
std::string text = "SampleApp Lab";
std::cout << text.substr(0, 9) << '\n';
std::cout << text.substr(10) << '\n';
return 0;
}#include <iostream>
#include <string>
int main()
{
std::string text = "hello";
for (char& ch : text)
{
if (ch == 'l')
{
ch = 'L';
}
}
std::cout << text << '\n';
return 0;
}