Intuition What this page does
The parent note told you that a C string ends at a hidden zero byte and that
strcat "finds the end, then copies". Here we draw every single byte and watch the machine
do it. By the last picture you will be able to run strcat in your head, box by box, and see
exactly where a buffer overflow is born.
Before we touch code, one promise about words we will use:
Definition The three words everything is built from
A byte is one little box that holds one number from 0 to 255. That's the smallest thing C
works with here.
A char ("character") is a byte we interpret as a letter using a code table (ASCII). The
box holding the number 72 means the letter H.
A char array is a row of these boxes sitting side by side in memory, each with an
address (a house number). char c[10] = a row of 10 boxes .
Definition The null terminator
'\0'
One special char whose number is 0. It is not the digit '0' (that's number 48). We draw
it as a box with STOP in it. Every C string function keeps walking the row until it lands on
this box. No STOP box = the function keeps walking into boxes that aren't yours.
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
We write out all 10 boxes of c and all 3 boxes of src, with their real ASCII numbers,
so nothing is invisible any more.
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).
Intuition WHY this matters
The whole trick of C strings is that the array size (10) and the string length (2) are two
different numbers . strcat cares about the STOP box, not about box 9.
strcat(dst, src) wants to append onto the end of dst. But dst is just an address — the
start box. Nothing tells it where the text stops. So the very first thing it does is scan
dst until it hits STOP . This is exactly strlen hiding inside.
i lands on STOP, not after it
We stop the moment the test fails, and it fails at the STOP box. So i=2 points exactly at
the old terminator — which is precisely the box we want to overwrite next. This is why the cost
of strcat is O ( len d s t ) : it re-walks the whole first string just to find this spot.
See Time Complexity for why doing this in a loop becomes O ( n 2 ) .
Now we copy src starting at box i=2. The first thing we write is src[0] = 'c' on top of
the old STOP box . The old terminator is deliberately destroyed.
Intuition WHY erase the old STOP
If we kept dst's old STOP, the string would still "end" at box 2 and the reader would never see
cd. The two words only become one word if the seam between them has no STOP in the middle .
The single STOP that survives at the very end must come from src.
We copy the next letter, then the next, each time stepping both i (in dst) and j (in
src) forward by one. Two rows, one shared rhythm.
Intuition WHY test the char
after writing it
This is the clever part beginners miss: we write first, ask questions second . That guarantees
the STOP box gets copied into dst before we quit — otherwise dst would have cd but no
terminator, and every future strlen would walk off the end. (See Undefined Behaviour in C .)
On the last step we copy src's STOP box into dst. The assignment's value is 0, so the test
!= '\0' is false , and the loop ends. Now dst reads abcd and terminates cleanly.
Worked example Verify with strlen
Walk c: a,b,c,d then STOP → count = 4 . strlen(c) returns 4. ✔
The parent's Forecast said 4 — this is why .
What if you append the empty string? src[0] is STOP immediately. We must show C handles this,
not crash.
Common mistake "Empty src does nothing, so it's always free"
Why it feels right: the content is unchanged. The trap: strcat still ran the whole
find-the-end walk over dst first (Step 2). Appending "" a thousand times in a loop is
still a thousand full scans of dst — pure wasted O ( n ) work each time.
Now the parent's warning, drawn. Take char buf[6] = "Hi"; and strcat(buf, "there"). The joined
word Hithere needs 2 + 5 + 1 = 8 boxes. buf only has 6 . Watch the copy march past box 5.
Common mistake The overflow, step by step
strcat has no idea how big buf is — it was handed only an address and a src. It writes
H i t h e r (boxes 0..5, still legal by luck) then e into box 6 and STOP into box 7 — both
past the array's end . C performs zero bounds checks, so it just... does it. Whatever lived in
boxes 6–7 (another variable, a saved return address) is now clobbered. That corrupted neighbour is
a buffer overflow — see Buffer Overflow & Memory Safety .
The fix: size for len+1, or use the seat-belt snprintf(buf, sizeof buf, "%s%s", a, b),
which stops writing at the box you told it about and always leaves a STOP.
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.
Recall Two-line self test
After char c[10]="ab"; strcat(c,"cd");, how many boxes does the string use and what is strlen(c)? ::: 5 boxes (a b c d STOP); strlen(c) is 4 (chars before STOP).
Why must dst hold strlen(dst)+strlen(src)+1? ::: The two words' letters plus the one final STOP terminator; miss the +1 and you overflow.
Pointers in C — strcat 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 O ( n ) ; looped, it's the O ( n 2 ) Shlemiel bug.
printf format specifiers — snprintf (the safe fix) shares %s %d %f.