Lesson 2 — Measuring, Indexing, Joining, Comparing
← Module home · ← Lesson 1 — Two ways · Lesson 3 — Find & modify →
In this lesson you will learn
length()/size()— and when an empty string matters- indexing a string and traversing it char by char (plus the range-for preview)
- concatenation —
+,+=, and the literal-on-the-left trap - comparison — the lexicographic rules, case sensitivity, and
==
1. length() — and the empty string
std::string s = "hello";
std::cout << s.length() << '\n'; // 5
std::cout << s.size() << '\n'; // 5 — identical, two names, one job
std::string t;
std::cout << t.length() << '\n'; // 0 — empty is a real, safe state
if (t.empty()) { ... } // the readable emptiness test
Length counts every char including spaces and punctuation — "Ayesha Khan" is 11. The empty string is legal and useful: getline returning "" (an empty line), a search with no result, an accumulator of text that hasn’t started.
2. Indexing — a string is a row of chars
Exactly like the arrays you own:
std::string word = "program";
// 0123456 ← indices (length 7, last index 6)
char first = word[0]; // 'p'
char last = word[6]; // 'm' — or word[word.length() - 1]
word[0] = 'P'; // UPDATE a box — "Program"
The same bounds law applies: valid indices are 0..length()-1. word[7] on a length-7 string is out of bounds — [] doesn’t check; .at(7) does (checked access — it ends the program with an exception rather than touching foreign memory; the safer default when an index’s validity isn’t proven):
word.at(7); // ends the program with a clear error — loud, not undefined
Traverse char by char — the workhorse of text processing:
for (int i = 0; i < word.length(); i = i + 1) { // note the int vs unsigned note below
std::cout << word[i] << '-';
}
// p-r-o-g-r-a-m-
The course style keeps the counting loop you know. (The modern range-for — for (char c : word) — is a preview you may meet in later code; it means “visit every char”. Both are fine; this unit drills the indexed form because analyzers need positions, not just chars.)
The signed/unsigned warning — one honest note
word.length() returns an unsigned type. Mixing it into signed arithmetic can produce warnings (e.g. i < word.length() - 1 when the string is empty computes a huge positive number). Two defences, both course-legal:
- guard emptiness first (
if (word.length() > 0)before anylength() - 1), or - compare with
intas shown (the compiler converts; a warning may appear — silence it by casting:i < static_cast<int>(word.length())).
The labs guard emptiness first — it’s also the logical fix.
3. Concatenation — building text
std::string a = "data", b = "base";
std::string c = a + b; // "database" — a NEW string
a += "base"; // append onto a — now "database"
+ makes a new string from two operands; += appends in place. Both feel natural — with one genuine trap:
std::string msg = "Total: " + 42; // ❌ compile error — no + for literal + int
std::string msg = std::to_string(42); // ✅ — conversion (Lesson 4)
std::string msg = "Total: " + std::string("(v2)"); // ✅ at least one std::string operand
Rule: + needs a std::string on at least one side. Two literals (or literal + number) don’t chain — and the left-to-right evaluation order means the first addition is the one that fails: "a" + "b" + s errors even though s is a string.
4. Comparison — the lexicographic rules
All six relational operators work, comparing text, box by box, left to right:
std::string a = "apple", b = "apply";
if (a == b) { ... } // false — differ at index 4
if (a < b) { ... } // true — 'e' (101) < 'y' (121)
The rules, in order of importance:
==compares content — nostrcmpneeded (the C-string trap is gone).- Ordering is lexicographic (dictionary) order by character codes: compare box by box; the first differing chars decide; a prefix is smaller than its extension (
"app" < "apple"). - Case matters:
'A'(65) <'a'(97) — so"Zebra" < "apple"is true, and"Apple" == "apple"is false. Case-insensitive comparison is a character-processing job (normalize both sides first). - Digits sort by code:
"10" < "9"is true —'1' < '9'— numeric order is a conversion job (Lesson 4), not a comparison job.
| Expression | Result | Why |
|---|---|---|
"cat" == "cat" |
true | content equal |
"cat" != "Cat" |
true | case differs |
"app" < "apple" |
true | prefix smaller |
"10" < "9" |
true | '1' < '9' |
"Zebra" < "apple" |
true | 'Z' < 'a' |
5. A complete worked program
// initials.cpp — indexing + concatenation
#include <iostream>
#include <string>
int main() {
std::string first, last;
std::cout << "First name: ";
std::cin >> first;
std::cout << "Last name: ";
std::cin >> last;
std::string initials;
initials += first[0]; // box 0 of each — the machine-grade answer
initials += last[0];
initials += '.'; // a char appends cleanly
std::cout << "Initials: " << initials << '\n'; // e.g. "A.K."
return 0;
}
Trace for Ayesha / Khan: initials = "" → A → AK → AK.. Three appends, each reading one box — indexing and concatenation composing.
Practice
- Exercises 8–13 — measure, index, join, compare
- Predictions 4–6
- Debugging 3–4
- Lab 1 — username validator · Lab 6 — name processor
Key takeaways
length()/size()count every char;empty()is the readable zero test — guard before anylength() - 1.- Strings index and traverse exactly like arrays;
.at()is the checked accessor. +/+=build text — astd::stringmust be on one side of each+.- Comparison is content-based, lexicographic, case-sensitive, and code-ordered for digits — normalize or convert before comparing what you mean.