Lesson 77 min read

Strings

C++ strings are what C strings dreamed of becoming β€” no more counting characters and null terminators.

The Old Way Was Painful

In C, a "string" was just an array of characters ending with a sneaky '\0' null terminator. Want to concatenate two strings? You'd better know their lengths, allocate enough memory, and call strcat() β€” hoping you didn't overrun the buffer. One mistake and you've got a security vulnerability.

std::string changed everything. It manages its own memory, knows its own length, and lets you do things like a + b to concatenate. It's what strings should have been from the start.

Creating Strings

There are many ways to create a string, and they all feel natural:

Creating & Concatenating Strings

#include <iostream>
#include <string>
using namespace std;
int main() {
// Different ways to create strings
string greeting = "Hello";
string name("World");
string repeated(5, '*'); // "*****"
string copy = greeting; // Makes a real copy
// Concatenation with +
string message = greeting + ", " + name + "!";
cout << message << endl;
// Append with +=
string sentence = "C++";
sentence += " is";
sentence += " powerful";
cout << sentence << endl;
// Size and length (they're identical)
cout << "Length: " << message.length() << endl;
cout << "Size: " << message.size() << endl;
cout << "Empty? " << boolalpha << message.empty() << endl;
return 0;
}
Output
Hello, World!
C++ is powerful
Length: 13
Size:   13
Empty?  false

Searching & Manipulating

Strings come loaded with methods for finding and modifying text. The key ones are find(), substr(), and replace(). Think of find() as a search bar β€” it returns the position of the first match, or string::npos if nothing is found.

Searching, Substring & Replace

#include <iostream>
#include <string>
using namespace std;
int main() {
string text = "The quick brown fox jumps over the lazy dog";
// find() returns position, or string::npos if not found
size_t pos = text.find("fox");
if (pos != string::npos) {
cout << "Found 'fox' at index " << pos << endl;
}
// substr(start, length)
string word = text.substr(16, 3); // "fox"
cout << "Extracted: " << word << endl;
// replace(start, length, new_string)
text.replace(pos, 3, "cat");
cout << "Replaced: " << text << endl;
// find a character
size_t spacePos = text.find(' ');
cout << "First space at: " << spacePos << endl;
return 0;
}
Output
Found 'fox' at index 16
Extracted: fox
Replaced: The quick brown cat jumps over the lazy dog
First space at: 3

Iterating Over Characters

#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main() {
string word = "Hello, World!";
// Range-based for loop
int upperCount = 0;
for (char c : word) {
if (isupper(c)) upperCount++;
}
cout << "Uppercase letters: " << upperCount << endl;
// Modify characters in place
string shout = "whisper";
for (char& c : shout) { // Note the & β€” we modify in place
c = toupper(c);
}
cout << shout << endl;
// Access individual characters
cout << "First char: " << word[0] << endl;
cout << "Last char: " << word.back() << endl;
return 0;
}
Output
Uppercase letters: 2
WHISPER
First char: H
Last char:  !

Comparing Strings

#include <iostream>
#include <string>
using namespace std;
int main() {
string a = "apple";
string b = "banana";
string c = "apple";
// == checks content equality (unlike Java!)
cout << boolalpha;
cout << "a == c: " << (a == c) << endl; // true
cout << "a == b: " << (a == b) << endl; // false
// < and > compare lexicographically
cout << "a < b: " << (a < b) << endl; // true (a comes before b)
cout << "b < a: " << (b < a) << endl; // false
// compare() returns 0 if equal, negative if less, positive if greater
cout << "compare: " << a.compare(b) << endl;
return 0;
}
Output
a == c: true
a == b: false
a < b:  true
b < a:  false
compare: -1

c_str() β€” Talking to C Code

Sometimes you need to pass a std::string to a C library function that expects a const char*. That's what c_str() is for β€” it gives you a null-terminated C-string pointer to the underlying data.

string_view β€” The Zero-Copy Reader (C++17)

std::string_view is a lightweight, non-owning view into a string. It doesn't allocate memory or copy characters β€” it just points at an existing string's data. This makes it perfect for function parameters when you only need to read the string.

string_view β€” Read Without Copying

#include <iostream>
#include <string>
#include <string_view>
using namespace std;
// Accepts both std::string and C-strings without copying
void printInfo(string_view sv) {
cout << "Text: " << sv << endl;
cout << "Length: " << sv.length() << endl;
cout << "First 5: " << sv.substr(0, 5) << endl;
}
int main() {
string str = "Hello from std::string";
const char* cstr = "Hello from C-string";
printInfo(str); // No copy!
cout << "---" << endl;
printInfo(cstr); // No copy!
return 0;
}
Output
Text: Hello from std::string
Length: 22
First 5: Hello
---
Text: Hello from C-string
Length: 19
First 5: Hello
Note: Use std::string_view for read-only string parameters β€” it avoids copying and works with both std::string and C-strings. Just be careful: a string_view doesn't own the data, so don't store it if the original string might be destroyed.

Raw String Literals β€” No More Escape Madness

#include <iostream>
#include <string>
using namespace std;
int main() {
// Regular string β€” escape everything
string path = "C:\\Users\\Alice\\Documents";
string json = "{\"name\": \"Alice\", \"age\": 30}";
// Raw string literal R"(...)" β€” no escaping needed!
string rawPath = R"(C:\Users\Alice\Documents)";
string rawJson = R"({"name": "Alice", "age": 30})";
cout << path << endl;
cout << rawPath << endl;
cout << json << endl;
cout << rawJson << endl;
// Multi-line raw strings
string html = R"(
<html>
<body>
<p>Hello!</p>
</body>
</html>)";
cout << html << endl;
return 0;
}
Output
C:\Users\Alice\Documents
C:\Users\Alice\Documents
{"name": "Alice", "age": 30}
{"name": "Alice", "age": 30}

<html>
  <body>
    <p>Hello!</p>
  </body>
</html>
Challenge

Quick check

What does find() return when the substring is NOT found?
← Arrays & VectorsFunctions β†’