Standard Library[lib]

C++ string (std::string) explained with examples

Section
Standard Library [lib]
Standard
C++17
Compilers
MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2, Clang 23.1 (libc++)
Published
Length
7 min read
In this answer8 clauses
  1. 1How do you create a string in C++?
  2. 2How do you get the length of a string in C++?
  3. 3How do you concatenate strings in C++?
  4. 4How do you read or change one character in a string?
  5. 5How do you find text in a C++ string? (find and npos)
  6. 6How do you get a substring in C++? (substr)
  7. 7How should a function take a string?
  8. 8The std::string functions you will use most

Abstract

A C++ string is a std::string, the standard library’s type for text: it owns its characters and grows as needed. Join strings with + or +=, get the length with size(), read a character with [] or the checked at(), and search with find(), whose result you compare with std::string::npos instead of testing it as a bool: a match at position 0 tests as false. Take a string you only read as const std::string& or, since C++17, std::string_view.

A row of six hatched cells, then three empty dotted cells, with a blue line where the row ends. Below, a shorter row of three hatched cells, with an arrow pointing up into the dotted cells.
Figure 1 — A string is a row of characters that can grow: += and append() copy another string’s characters onto its end.

A C++ string is a std::string, the standard library’s type for text, declared in the header <string>. It owns its characters, grows as needed and frees its memory by itself. Create one with std::string name = "Ada";, join strings with +, get the length with size(), and search with find(), which returns std::string::npos when nothing is found.

[Example 1:
#include <iostream>
#include <string>

int main() {
    std::string name = "Ada";
    std::string greeting = "Hello, " + name + "!";
    std::cout << greeting << '\n';                    // Hello, Ada!
    std::cout << "size: " << greeting.size() << '\n'; // size: 11
}

— end example]

It printed:

[Example 2:
Hello, Ada!
size: 11

— end example]

This answer is written for C++17, which added std::string_view (Since When’s C++17 table). The samples were built with MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2 and Clang 23.1 (libc++), and all four printed the same unless a sentence says otherwise.

How do you create a string in C++?

You create a std::string from a literal, empty, from one character repeated, or as a copy of another string:

[Example 3:
std::string email = "ada@example.com";  // from a literal
std::string blank;                      // empty: size 0
std::string line(10, '-');              // 10 copies of '-'
std::string copy = email;               // its own characters

copy[0] = 'A';                          // email is unchanged

— end example]

A copy owns its characters, so after the change email still printed ada@example.com and copy printed Ada@example.com. An empty std::string is a valid string of length 0, not a null pointer: even its c_str(), which gives the text as a C string, was not null. Each sample below starts from this email.

How do you get the length of a string in C++?

Call size() or length(): they return the same number, 15 for "ada@example.com", and empty() tells you whether that number is 0. The number counts bytes (chars), not letters. The word “café”, written as its UTF-8 bytes "caf\xC3\xA9", has a size() of 5 for four letters: UTF-8 stores the é in two bytes.

How do you concatenate strings in C++?

For string concatenation, use + to make a new string, and += or append() to add to an existing one; push_back() adds a single character:

[Example 4:
std::string user = "ada";
std::string domain = "example.com";

std::string email = user + "@" + domain;   // a new string
std::cout << email << '\n';                // ada@example.com

std::string greeting = "Hello";
greeting += ", ";              // adds to greeting itself
greeting.append(user);         // the same, as a function
greeting.push_back('!');       // one character
std::cout << greeting << '\n'; // Hello, ada!

— end example]

append() has no overload that takes one char, so add one with push_back() or append(1, '!').

One side of + must be a std::string. A literal such as "Hello, " is an array of const char, not a std::string, and + has no overload for two of them:

[Example 5:
std::string s = "Hello, " + "world";   // error: two literals

— end example]

GCC’s error names both array types: invalid operands of types 'const char [8]' and 'const char [6]' to binary 'operator+'. MSVC says '+': cannot add two pointers. Make one side a string, std::string("Hello, ") + "world", and it works. That is also why the first example compiled: "Hello, " + name has a std::string on one side, its result is a std::string, and so the + "!" after it has one too.

How do you read or change one character in a string?

Write s[i] to read or change the character at position i, counting from 0, or s.at(i) to have the position checked. Past the end, [] is undefined behavior, meaning the program may do anything, while at() throws std::out_of_range:

[Example 6:
std::cout << email[0] << '\n';       // a: positions start at 0
std::cout << email.back() << '\n';   // m: the last character
email[0] = 'A';                      // change one character
std::cout << email << '\n';          // Ada@example.com

int dots = 0;
for (char c : email) {               // every character in turn
    if (c == '.') ++dots;
}
std::cout << "dots: " << dots << '\n';   // dots: 1

try {
    std::cout << email.at(99) << '\n';   // checked: throws
} catch (const std::out_of_range& e) {
    std::cout << "out_of_range: " << e.what() << '\n';
}

— end example]

The range-for loop, for (char c : email), hands you each character in turn, here to count the dots. All four caught the std::out_of_range from at(99); each standard library words the message from what() its own way.

How do you find text in a C++ string? (find and npos)

find() returns the position of the first match, or std::string::npos when there is none, so compare the result with npos:

[Example 7:
std::cout << email.find('@') << '\n';         // 3
std::cout << email.find("example") << '\n';   // 4

std::size_t pos = email.find("xyz");
if (pos == std::string::npos) {               // the right test
    std::cout << "xyz: not found\n";
}
std::cout << std::string::npos << '\n';       // the largest size_t

if (email.find("ada")) {             // wrong: the match is at 0
    std::cout << "ada: found\n";
} else {
    std::cout << "ada: not found\n"; // printed, though it is there
}
if (email.find("xyz")) {             // npos is not 0, so true
    std::cout << "xyz: found\n";     // printed, though it is not
}

— end example]

npos is the largest value of the unsigned type that positions use: on a 64-bit build, all four printed 18446744073709551615. A test such as if (email.find("ada")) asks whether the position is nonzero, not whether there was a match. A match at position 0 converts to false, and npos, being nonzero, converts to true, so the program printed ada: not found and xyz: found. No compiler warned about either test, at /W4 or at -Wall -Wextra.

The text ada@example.com in fifteen cells numbered 0 to 14. An arrow labelled find(‘@’) returns 3 points at the @ in cell 3. Brackets under the numbers mark substr(0, 3), giving ada, and substr(4), giving example.com.
Figure 2 — Positions start at 0: find() returns one of them, and substr() cuts between them.

How do you get a substring in C++? (substr)

substr(pos, count) returns a new string holding count characters from position pos; leave count out to take everything to the end. With find(), it splits the address in two:

[Example 8:
std::size_t at = email.find('@');            // 3
std::string user = email.substr(0, at);      // 3 characters from 0
std::string domain = email.substr(at + 1);   // from 4 to the end
std::cout << user << '\n';                   // ada
std::cout << domain << '\n';                 // example.com

std::cout << email.substr(4, 100) << '\n';   // cut short at the end
try {
    std::cout << email.substr(99) << '\n';   // past the end: throws
} catch (const std::out_of_range& e) {
    std::cout << "out_of_range: " << e.what() << '\n';
}

— end example]

A count that runs past the end is cut short, so substr(4, 100) printed example.com. A pos past the end throws std::out_of_range. The substring is a copy: changing user leaves email as it was.

How should a function take a string?

Take a string the function only reads as const std::string& or, since C++17, as std::string_view, a view of characters someone else owns. Passed a std::string, neither copies the text. Passed a literal, they differ: a const std::string& parameter needs a temporary std::string built from it, a copy of the text, while a std::string_view points at the literal’s own characters.

[Example 9:
std::string_view domain_of(std::string_view email) {
    return email.substr(email.find('@') + 1);
}

— end example]

When to pass by value or by const reference covers every other parameter type.

The std::string functions you will use most

Each example works on email unless it names another string.

Call What it does Example Result
size(), length() the number of chars email.size() 15
empty() whether the size is 0 email.empty() false
+, +=, append() join; += and append() change the left string user + "@" + domain ada@example.com
[], at() one character; at() checks the position email[0] a
find() the first match, or npos email.find('@') 3
rfind() the last match rfind('.') on ada.lovelace@example.com 20
substr() a copy of part of the string email.substr(0, 3) ada
insert() put text in at a position email.insert(3, ".lovelace") ada.lovelace@example.com
erase() remove characters erase(3, 9) on ada.lovelace@example.com ada@example.com
replace() swap characters for other text email.replace(4, 7, "school") ada@school.com
clear() remove every character email.clear() size 0
starts_with(), ends_with() (C++20) test the start or the end email.ends_with(".org") false
contains() (C++23) test for a match anywhere email.contains('@') true

Numbers to and from strings have their own answers: C++ string to int with std::stoi, and C++ int to string with std::to_string. To read a line of input into a string, use std::getline; to compare two, see C++ string comparison. Standard library has the answers on the library’s other types.

Questions

What is the difference between a C string and std::string?

A C string is an array of char that ends in a null character, and its size is fixed when the array is made. A std::string manages its own memory, grows as text is added and knows its length. Its c_str() member gives you the text as a C string when a function needs one.

Is size() the same as length() for a C++ string?

Yes. Both return the number of char elements in the string as an unsigned number, and both printed 15 for “ada@example.com” on all four compilers. The count is in bytes, so a word stored as UTF-8 can have more chars than letters.

What is std::string::npos?

It is the value find() returns when it finds nothing, and the largest value of the unsigned type that string positions use: 18446744073709551615 on a 64-bit build. Passed as a count, as in substr(pos, npos), it means “to the end”. Test a search with == std::string::npos, not as a bool, because a match at position 0 converts to false.

How do you convert a string to an int in C++?

Call std::stoi(s), which returns the number the text spells: std::stoi(“42″) is 42. std::to_string(42) goes the other way and returns the string “42″. Cpp Next has an answer for each direction, including what std::stoi does with text that is not a number.

How do you compare two strings in C++?

Use ==, which compares the text when one side is a std::string: email == “ada@example.com” is true when email holds exactly those characters, and != is its opposite. Between two C strings, == compares their addresses instead.

How do you read a line of text into a string?

Call std::getline(std::cin, line), where line is a std::string. Unlike std::cin >> line, which stops at the first space, it reads the whole line into line and drops the newline at its end.

Sources

  1. std::basic_stringcppreference.com
  2. std::basic_string::findcppreference.com
  3. std::basic_string::nposcppreference.com
  4. std::basic_string::substrcppreference.com
  5. std::basic_string::atcppreference.com
  6. std::operator+ (std::basic_string)cppreference.com
  7. std::basic_string_viewcppreference.com

Read next

  1. C++ string comparison: ==, compare() and strcmpStandard Library · Miriam Castellanos · written for C++17
  2. C++ string to int: std::stoi or std::from_chars, not atoiStandard Library · Miriam Castellanos · written for C++17
  3. C++ random number: how to generate one in a rangeStandard Library · Miriam Castellanos · written for C++11