5.1.20 · D2C Programming

Visual walkthrough — String handling — char arrays, null terminator, strcpy, strcat, strlen, sprintf (dangers)

2,217 words10 min readBack to topic

Before we touch code, one promise about words we will use:

Our running example, used in every figure below:

char c[10] = "ab";   // we will do  strcat(c, "cd")  and get "abcd"
char src[3] = "cd";  // the little string we append

Step 1 — Lay out the memory: what "ab" really looks like

char c[10] = "ab"; fills box 0 with a, box 1 with b, box 2 with STOP, and — because the array is bigger than the text — leaves boxes 3–9 as leftover junk (whatever was in memory before).

Figure — String handling — char arrays, null terminator, strcpy, strcat, strlen, sprintf (dangers)

Step 2 — strcat can't see the end; it must walk to it

Figure — String handling — char arrays, null terminator, strcpy, strcat, strlen, sprintf (dangers)

Step 3 — Overwrite the old STOP with the first new letter

Figure — String handling — char arrays, null terminator, strcpy, strcat, strlen, sprintf (dangers)

Step 4 — Keep copying, moving both counters together

Figure — String handling — char arrays, null terminator, strcpy, strcat, strlen, sprintf (dangers)

Step 5 — The final STOP arrives, and the loop quits

Figure — String handling — char arrays, null terminator, strcpy, strcat, strlen, sprintf (dangers)

Step 6 — The degenerate case: src is empty ("")

Figure — String handling — char arrays, null terminator, strcpy, strcat, strlen, sprintf (dangers)

Step 7 — The disaster case: dst is one box too small

Figure — String handling — char arrays, null terminator, strcpy, strcat, strlen, sprintf (dangers)

The one-picture summary

Figure — String handling — char arrays, null terminator, strcpy, strcat, strlen, sprintf (dangers)

This single figure stacks the whole life of strcat(c,"cd"): (1) the two rows laid out with their STOP boxes, (2) the arrow walking dst to find its STOP, (3) that STOP being erased, (4) cd streaming in, (5) one final STOP closing the joined word abcd — with the danger line marked where a too-small buffer would have spilled past its last legal box.

Recall Feynman retelling — say it like a story

Imagine two rows of mailboxes. The first spells ab and has a red STOP flag right after, then some empty spare boxes. The second spells cd with its own STOP flag. I want one long word. First I walk along row one, box by box, until I reach its STOP flag — that flag tells me "the word ends here." I stand on that STOP box. Now I start copying row two into row one starting on that very STOP box: I paint c over the old flag (the seam must have no flag, or the word would end too early), then d in the next box, then I copy row two's STOP flag as the new single ending. Done: abcd with exactly one flag at the end. But if row one only had room for six boxes and my joined word plus flag needs eight, I keep painting into box seven and eight — boxes that belong to my neighbour. Nobody in C stops my hand. That neighbour-scribble is the buffer overflow, and it all comes from forgetting that the STOP flag costs one extra box.

Connections

  • Pointers in Cstrcat only receives addresses, which is why it can't know sizes.
  • Arrays vs Pointers — the array c[10] knows its size at compile time; the pointer inside strcat does not.
  • Buffer Overflow & Memory Safety — Step 7 is the birth of the classic exploit.
  • Undefined Behaviour in C — writing past box 9 is UB; the machine won't warn you.
  • Time Complexity — the find-the-end walk makes strcat ; looped, it's the Shlemiel bug.
  • printf format specifierssnprintf (the safe fix) shares %s %d %f.