Arrays, Strings, and Algorithmic Basics
Manipulate collections of data and reason about elementary searching and sorting.
Content
Strings and Null Terminators
Versions:
Watch & Learn
AI-discovered learning video
Sign in to watch the learning video for this topic.
Strings and Null Terminators — The Tiny NUL That Runs the World
"The string isn't the letters — it's the little NUL that says, ‘Stop.’" — your future debugger
You're already familiar with arrays and indexing from the previous section. Now we take that array-of-chars idea and add one magical sentinel: the null terminator (written as \0). That single byte is what makes C strings behave like strings instead of a pile of characters that keep going until the program panics.
What is a C string (really)?
- A C string is an array of char that has a
\0character at the end. - That
\0byte (value 0) marks the end of text. Functions likestrlen,printf("%s"), andstrcpyrely on it.
Micro explanation
Imagine an array as a train of boxcars. The null terminator is the caboose with a giant STOP sign. Without the caboose, the train keeps going — and eventually you’ll run into memory you don't own.
Memory layout & off-by-one (the place most bugs hide)
Consider:
char s[] = "Hi"; // compiler stores {'H', 'i', '\0'} — that's 3 bytes
Indexing: s[0] = 'H', s[1] = 'i', s[2] = '\0'. If you allocate char s[2]; and then do strcpy(s, "Hi"); — you overflow. Always allocate length + 1 for the NUL.
Common mistake:
char name[4];
strcpy(name, "Dave"); // "Dave" needs 5 bytes: 'D','a','v','e','\0' -> overflow
Why this bites you: strlen walks memory until it finds a \0. If none exists in the buffer, strlen runs off into neighboring memory — undefined behavior: crashes, garbage, security holes.
String literals vs arrays
| Declaration | Mutability | Where stored | Null added? |
|---|---|---|---|
char *s = "hello"; |
Immutable (UB to modify) | Static area (string literal) | Yes (compiler provides \0) |
char s[] = "hello"; |
Mutable | Stack (or data) — copied | Yes (copied with \0) |
Important: modifying a string literal is undefined. If you need to change characters, use the array form.
Common functions & gotchas
strlen(s)— counts bytes until\0. If\0missing, UB.strcpy(dest, src)— copies including\0. Danger: no length check.strncpy(dest, src, n)— copies up tonbytes; ifsrclength >=n, result is not NUL-terminated. You must manually adddest[n-1] = '\0'.snprintf(dest, size, "%s", src)— safer: always NUL-terminates when size > 0.gets()— DO NOT USE. Removed from C11. Usefgets.fgets(buf, size, stdin)— reads at mostsize-1chars and NUL-terminates; may include the newline.scanf("%s", buf)— stops at whitespace but can overflow; use a width:scanf("%19s", buf).
Example showing strncpy pitfall:
char a[5];
strncpy(a, "HelloWorld", 5);
// a now contains {'H','e','l','l','o'} and NO '\0' — dangerous
// fix:
a[4] = '\0';
Better modern pattern: snprintf(a, sizeof a, "%s", src); — it truncates and NUL-terminates.
Buffer overflow & security
Because strings rely on \0 instead of a stored length, C is extremely efficient — but also dangerous. Overwriting the NUL or writing past a buffer leaks into adjacent memory. This is how classic vulnerabilities (stack smashing, return address overwrite) happen.
Rule of thumb:
- Always allocate room for the NUL:
len + 1. - Prefer bounded functions (
snprintf,strncatcarefully,strlcpyif available). - Validate input sizes.
Debugging strings — practical tips (gdb friendly)
When something prints garbage or crashes, you want to inspect memory and find where \0 went missing.
- In gdb, to print a C string at an address:
x/s <address>orp (char*)ptr. - To watch a buffer as you write it: set a breakpoint and
x/32bx bufferto see bytes — look for00bytes (NUL).
Example commands (you probably used gdb earlier):
(gdb) b main
(gdb) run
(gdb) x/s mybuf # prints string until \0
(gdb) x/16bx mybuf # shows raw bytes; check for 0x00
That raw-byte view helps you see if the NUL is missing or if a stray character replaced it.
Examples: wrong vs right
Wrong (off-by-one overflow):
char s[5];
strcpy(s, "Hello"); // needs 6 bytes -> overflow
Right:
char s[6]; // 'H','e','l','l','o','\0'
strcpy(s, "Hello");
Reading user input safely:
char name[20];
if (fgets(name, sizeof name, stdin)) {
// remove newline if present
name[strcspn(name, "\n")] = '\0';
}
This uses strcspn to find newline and replace it with \0 — compact and safe.
Quick checklist (for when your program explodes)
- Did you allocate
len + 1bytes? ✅ - Are you using
strcpywithout bounds? Replace withsnprintforstrlcpy. ✅ - Did you use
strncpyand forget to add a\0? ✅ - Are you accidentally modifying a string literal? Use array form if you need mutation. ✅
- When debugging, use
x/sandx/xbin gdb to inspect the\0. ✅
"A missing
\0is like forgetting to close parentheses — the compiler doesn't care, but the runtime will scream."
Takeaways — the things to memorize
- A C string = array of char + terminating
\0. - Always size buffers for the
\0(length + 1). - Many string functions assume
\0— if it's missing, undefined behavior follows. - Use bounded APIs (
snprintf,fgets) and validate lengths. - When debugging, inspect raw bytes to find the lost
\0.
If you take away one single piece of wisdom: the null terminator is tiny but non-negotiable. Treat it like an invisible semicolon at the end of your string — forget it and the runtime will file a formal complaint.
Want a quick practice problem?
Write a function void safe_copy(char *dst, size_t dst_size, const char *src) that copies src into dst safely and always NUL-terminates. Hint: snprintf or strncpy + manual dst[dst_size-1] = '\0'.
Try it, compile with your Makefile from the previous section, and when it fails, break in gdb and x/32bx your buffer. You'll see the beauty of \0 in action.
Comments (0)
Please sign in to leave a comment.
No comments yet. Be the first to comment!