Strings

Course 1 · Ch 8
Strings
Arrays and pointers, applied to text — including the bug that shaped decades of security history

C has no dedicated string type. Everything Chapters 6 and 7 covered — fixed-size arrays, no bounds checking, pointer arithmetic — applies directly to text, exactly as-is. This closing chapter of Course 1 is where those two chapters' consequences become the most historically significant.

Strings Are char Arrays

char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

A string is just an array of char, nothing more.

Null Termination

The '\0' byte marks the end. There is no separate length field anywhere — C strings carry no length of their own. Rust's String stores its length directly, alongside the data; finding a C string's length means scanning forward until '\0' is found, an O(n) operation every single time, unlike Rust's O(1) length lookup.

String Literals vs. Char Arrays

char *literal = "hello"; // pointer to read-only memory — modifying it is UB char mutable_str[] = "hello"; // a genuine, mutable copy — safe to modify

A real, important distinction: char *s = "hello" points at a string literal, which may live in read-only memory — writing through s is undefined behavior. char s[] = "hello" copies the characters into a genuinely mutable local array.

string.h Functions

#include <string.h> size_t len = strlen(s); // scans for '\0', returns the length strcat(dest, src); // appends src onto the end of dest int same = strcmp(a, b); // 0 if equal, nonzero otherwise

strcpy vs. strncpy — The Classic Buffer Overflow

strcpy copies a string with zero bounds checking — exactly Chapter 6's array-overflow problem, applied specifically to text. Copying a longer string into a smaller buffer silently writes past its end. This single mistake is one of the most historically significant vulnerability classes in software security.

char small[5]; strcpy(small, "This is way too long"); // buffer overflow — UB

strncpy takes a maximum length and is safer — but has its own genuine gotcha: if the source is at least n characters long, strncpy does not guarantee the result is null-terminated at all.

char buf[5]; strncpy(buf, "Hello!", 5); // copies "Hello" — no room left for '\0'
ConceptRust StringC
Length trackingstored directly — O(1) lookupnone — scan for '\0', O(n) every time
Bounds-checked copyalways, by designstrcpy — never; strncpy — partially, with its own gotcha
Mutability of a literaln/a — owned data is always mutable if declared soa string literal is read-only; modifying it is UB
Always leave room for the null terminator
A buffer meant to hold "hello" (5 characters) needs 6 bytes, not 5 — one extra for '\0'. Forgetting this is one of the most common sizing mistakes in C.
strncpy isn't fully safe either
If the source string's length is >= n, strncpy copies exactly n bytes and stops — with no guarantee the result is null-terminated. Treating strncpy as a fully "safe" drop-in replacement for strcpy is itself a real, common mistake.

Coding Challenges

Challenge 1

Declare a char array using the char s[] = "..." form, print it, modify one character, and print it again to confirm the modification worked.

📄 View solution
Challenge 2

Write a function that computes a string's length by manually scanning for the null terminator (without calling strlen), and compare its result against the real strlen() for a test string.

📄 View solution
Challenge 3

Explain the specific gotcha with strncpy that makes it not a fully safe replacement for strcpy, and describe what a caller must do afterward to guarantee the result is properly null-terminated.

📄 View solution

Chapter 8 Quick Reference — Course 1 Complete

  • A C string is just a char array — no dedicated string type exists
  • '\0' marks the end — no separate length is ever stored, unlike Rust's String
  • char *s = "..." is a read-only literal; char s[] = "..." is a genuine mutable copy
  • strcpy — zero bounds checking, the classic buffer-overflow source
  • strncpy — bounded, but doesn't guarantee null-termination if the source is >= n characters
  • Always size a buffer for the content plus one byte for '\0'
  • Course 1 complete. Course 2 picks up with structs, unions, and dynamic memory — C's own central safety-tradeoff chapter