TRT
Sign in

Foundations · Module 3 · Lesson 5 of 10

Strings: C-Strings and std::string

See how text is stored as numbers in memory, how a C-string marks its end with a zero byte, and how std::string and std::string_view take over the length, memory and safety work.

FreeAbout 9 minutes

By the end you can

  • Explain how a C-string marks its end and what finding its length costs
  • Predict what happens when a C-string's terminator is missing
  • Choose between a C-string, std::string and std::string_view for a task
  • Predict which string comparisons and concatenations work and which are traps

Builds on Types Decide What Bits Mean, How a Vector Grows: Size, Capacity and Reallocation.

01 · A question first

The word Hello has five characters, yet the string literal in your program occupies six bytes. What is the extra byte for? How does a function learn how long a text is when nothing stores its length? And what breaks when that extra byte is missing?

Make a guess, then step through the trace below to check it.

02 · Watch it run

Where does a C-string end?step 1 of 4
char a[3] = {'H', 'i', '\0'};char b[2] = {'H', 'i'};size_t n = strlen(a);size_t m = strlen(b);
  1. byte 048(looked at in this step)
  2. byte 169(looked at in this step)
  3. byte 200(looked at in this step)
  4. byte 3--
  5. byte 4--
  6. byte 5--
  7. byte 6--
  8. byte 7--

A C-string is an array of char that ends with a zero byte. Letters are numbers, as in lesson 4: H is 72, which is 48 in hexadecimal, and i is 105, or 69. The array a takes three bytes, and the last one is the terminator, 00. The eight cells are bytes of memory.

Machine work: store 48, 69 and the terminator 00

03 · The idea

Start with the problem

Text is everywhere: names, messages, file contents. But a computer stores only numbers, so text is a sequence of numbers, one per character, as lesson 4 showed. Two questions follow. Where does the sequence end, and who looks after the memory it occupies?

C answers the first with a marker byte and leaves the second to you. C++ adds a class that answers both. Here are the two styles side by side:

cpp
const char* greeting = "Hello";           // C-style: a pointer to the characters
std::string name = "Ada";                 // C++ style: an object that owns them
std::string full = name + " says " + greeting;

The first line uses a pointer, which lesson 6 explains. For now, read const char* as "the address of the first character".

How the idea works

Characters are numbers. A char is a one-byte integer. In ASCII, 'A' is 65, 'a' is 97 and '0' is 48. That makes character arithmetic possible: c - '0' turns the digit character c into its numeric value, and c >= 'a' && c <= 'z' tests for a lowercase letter.

C-strings. A C-string is an array of char whose last element is a zero byte, written '\0' and called the terminator. The string literal "Hi" therefore has three bytes: two letters and the terminator. The literal is stored in read-only memory inside the executable, ready before the program starts, which is the string literal you met in lesson 2. Nothing stores the length. The function strlen, from the header <cstring>, finds it by scanning for the zero, as steps 1 to 3 of the trace show. The same header offers strcpy, strcmp and strcat. If the terminator is missing, the scan runs on into neighboring memory (step 4).

std::string. Declared in <string>, it owns its characters, stores its own size, manages its capacity the way a vector does, and keeps the terminator in place for you:

cpp
#include <string>
 
std::string s = "Hello";
s += ", world";                       // append: "Hello, world"
std::cout << s.size();                // 12
std::string t = s.substr(0, 5);       // "Hello"
std::size_t pos = s.find("world");    // 7
bool same = (s == t);                 // false: == compares contents

Strings have value semantics like vectors: copies are independent, and they are passed by const reference.

Small strings are stored inside the object. A std::string keeps its characters on the heap only when there are many. With libstdc++ the object is 32 bytes, and texts up to 15 characters live inside it with no allocation. libc++ uses 24 bytes and fits up to 22 characters. This small string optimization matters, because most strings in programs, such as names and keys, are short.

std::string_view. From C++17, a std::string_view is a pointer plus a length: a read-only window onto characters that someone else owns. It accepts a literal, a std::string or a part of one, without copying, which makes it the right parameter type for functions that only read text.

Bytes, not letters. A std::string holds bytes. With ASCII, one byte is one letter. In UTF-8, which most systems use, a letter such as é takes two bytes, so size() counts bytes, not letters. For full Unicode handling you will need a library.

Why it is correct

The terminator convention lets strings have any length with no extra storage, because C chose to spend one byte per string instead of a length field. The price is that the length is never known for free, and every function that handles the text must trust that the terminator is there.

std::string moves that trust into one carefully tested place. The class maintains the invariant that the characters, the size and the terminator agree. Appending grows the buffer the way lesson 4 described, copying duplicates the characters, and the destructor frees them. So the dangerous parts, tracking length and managing memory, are written once in the library and not by hand in every program.

What it costs

Finding the length. For a C-string it costs time proportional to the length. A loop such as for (size_t i = 0; i < strlen(s); ++i) can call strlen on every iteration. Compilers sometimes hoist the call, but for a million-character string the worst case is a trillion steps. A std::string knows its size already.

Building text. Writing s = s + x in a loop creates a new string on every pass and copies all the old characters, which adds up to quadratic time. Writing s += x appends in place with the amortized constant cost of lesson 4.

Memory. A std::string costs 32 bytes with libstdc++ even when empty, but needs no heap allocation for short text. A long text needs one allocation, typically tens to hundreds of nanoseconds. A std::string_view costs 16 bytes and copies nothing.

Pitfalls and variations

Overflowing a char array. Copying a long name into char name[5] with strcpy writes past the end, which is the out-of-bounds write of lesson 2. Use std::string.

Comparing pointers. == on two const char* values compares addresses. It compiles and gives surprising answers. Compare std::string values, or use strcmp.

Adding two literals. "abc" + "def" does not compile, because it tries to add two pointers. Make one side a std::string, as in std::string("abc") + "def".

Views that outlive their text. A std::string_view of a temporary dangles once the temporary is destroyed, and so does a pointer obtained from c_str() after the string changes or dies. Keep the owner alive for as long as the view is used.

Unchecked indexing. s[i] is unchecked, like an array. The call s.at(i) is checked. And s.size() returns an unsigned value, so the comparison warning of lesson 4 applies.

Variations. The function std::to_string converts numbers to text, and std::stoi converts back, throwing on bad input. std::format, from C++20, builds text from a pattern. Raw string literals, written R"(...)", avoid escape characters. Wide and UTF types such as std::u8string exist for Unicode work.

Where it is used

Text appears in nearly every program: parsing input, building messages, file names, logging, and network protocols. C interfaces take const char*, and s.c_str() gives them one. Libraries use std::string_view for read-only parameters, to avoid copies.

The next lesson, "Pointers: Variables That Hold Addresses", explains the const char* that you have now seen several times, and shows what an address in a variable can do.

04 · Prove it

Question 1 of 4 · answer from memory

How many bytes does the string literal Hi, with two letters, occupy in memory, and how does `strlen` find its length?

05 · Practice

Something wrong with this lesson? Report a problem

Not started

Sign in to save your learning progress.

Sign in to save