C
Strings
C has no dedicated string type. What every other language calls a "string," C represents as a plain
char array with one special rule: it ends with a hidden '\0' (NUL) byte marking
where the text stops. Understanding that one convention explains almost everything else about how C
strings behave — and misbehave.
- What it is
- A
chararray whose contents are text, terminated by a'\0'(NUL, the number zero) byte. - Mental model
- A string is just an array of characters with a "stop reading here" marker at the end — every string function's real job is finding or respecting that marker.
- Key numbers
- A string holding N visible characters needs an array of at least N + 1 bytes — the
+1is always for the NUL terminator. - Reach for it when
- Storing or manipulating text — but always with the terminator and buffer size held firmly in mind, unlike a self-tracking string type in other languages.
The NUL terminator
'\0' is a single byte with the numeric value 0 — not the character
'0' (which is a completely different byte, value 48). Every C string function that walks a
string — printing it, measuring its length, copying it — keeps reading bytes until it hits this one
specific value, then stops.
Declaring a string
char greeting[4] = "Hi!"; // explicit size — must include room for '\0'
char greeting2[] = "Hi!"; // size 4, inferred automatically from the literal
char *greeting3 = "Hi!"; // a pointer to a read-only string literal
printf("%s\n", greeting); // %s prints characters until it hits '\0'
The first two lines create an actual, modifiable array on the stack. The third creates a pointer to a
string literal that typically lives in read-only memory — attempting to modify greeting3[0]
is undefined behavior, often crashing immediately on a desktop OS (and silently corrupting flash on some
embedded targets).
Common string.h functions
| Function | Does | Example |
|---|---|---|
strlen(s) | Counts characters before the NUL (not including it) | strlen("Hi!") → 3 |
strcpy(dst, src) | Copies src into dst, including the terminator | strcpy(buf, "Hi!") |
strcat(dst, src) | Appends src onto the end of dst | strcat(buf, "!") |
strcmp(a, b) | Returns 0 if equal, nonzero otherwise | strcmp("a", "b") → negative |
strncpy(dst, src, n) | Like strcpy, but copies at most n bytes | strncpy(buf, src, sizeof(buf)) |
#include <string.h>
#include <stdio.h>
int main(void) {
char name[20] = "Ada";
printf("Length: %zu\n", strlen(name)); // Length: 3
if (strcmp(name, "Ada") == 0) {
printf("Match!\n");
}
return 0;
}
Reading string input safely
Reading text into a fixed-size buffer always risks overflowing it if the input is longer than expected —
always give a length limit, and never trust gets() (removed from the language entirely in
C11 for exactly this reason):
char name[20];
fgets(name, sizeof(name), stdin); // reads at most 19 chars + NUL — safe
// scanf's %s is also unsafe without a width limit:
scanf("%19s", name); // limits to 19 chars + NUL — matches the buffer size
Real-world applications
- Command parsing — comparing a received string against a set of known commands with
strcmp, common in simple serial/UART command interfaces. - Log/debug messages — building a formatted status string with
snprintfbefore sending it out a UART for debugging. - Configuration parsing — reading a line of text and splitting it into fields, common in bootloader and configuration-file code.
Gotchas
- The missing-NUL bug — building or copying a string without leaving room for (or without actually writing) the terminating
'\0'means every function that reads it keeps going past the end of the buffer into whatever memory follows, until it happens to find a zero byte somewhere. This is a real, common source of crashes and information leaks, not a theoretical concern. strcpyandstrcatperform no bounds checking on the destination buffer — copying a long string into a short buffer silently overwrites whatever memory comes after it (a classic buffer overflow). Preferstrncpy/snprintfwith an explicit size, and always double-check the size actually accounts for the terminator.- Comparing two strings with
==compares their addresses, not their contents — it almost never does what a beginner expects. Always usestrcmpfor content comparison. char greeting[4] = "Hi!!";(a 4-character literal into a 4-byte array) compiles in some compilers with a warning, but leaves no room for the terminator at all — the array is exactly full of visible characters with nowhere for'\0'to go, guaranteeing every string function overruns it.