How strings are represented in C
In C, a string is a character array terminated by a null byte '\0'. Because arrays decay to pointers when passed to expressions, strings are commonly traversed using a pointer to the first element. Understanding this representation is essential for reliably iterating through each character, whether you use index-based access or pointer arithmetic.
Core approaches to iterate through a string
You can iterate through a C string by index into a character array, by advancing a pointer until the null terminator, or by combining pointer indexing with explicit length limits. Each approach affects readability, bounds safety, and whether the original pointer is modified.
Index-based traversal with a for loop
Use an integer index to access each element until the null terminator is reached. This keeps the original array or pointer unchanged and makes the termination condition explicit.
const char *s = "hello";
for (size_t i = 0; s[i] != '\0'; i++) {
/* process s[i] */
}Pointer traversal without an index
Advance a pointer (or a copy of it) until the null byte. Dereference only when not at the terminator. This method is concise but modifies the pointer if you do not keep a backup.
const char *s = "hello";
while (*s != '\0') {
/* process *s */
s++;
}Using strlen to bound an index loop
Compute the length first, then iterate over known positions. Note that this traverses the string twice and does not protect against embedded null bytes.
const char *s = "hello";
size_t len = strlen(s);
for (size_t i = 0; i Safety and bounds considerations
Always ensure iteration stops at the null terminator or at a known limit to avoid reading past allocated memory. For arrays of possibly incomplete initialization, prefer explicit length or sentinel checks and avoid assuming content past the terminator. When working with externally defined buffers, validate lengths and consider buffer boundaries.
Compact examples and comparison
The following table summarizes common styles, their side effects on the pointer, whether they scan twice, and typical use cases.
| Method | Pointer changed | Scans string twice | Typical use case |
|---|---|---|---|
| Index with sentinel check | No | No | Readable, preserves original pointer |
| Pointer advance | Yes | No | Compact traversal; loses original position |
| strlen + index | No | Yes | When length is needed separately |
| do-while for non-empty strings | Yes if advancing original | No | At-least-once processing |
Common pitfalls and fixes
- Missing null check: looping until
1instead of'\0'causes overrun. - Modifying a string literal: writing into a string literal is undefined behavior; use character arrays when mutation is required.
- Off-by-one errors: ensure enough space for the null terminator when copying or building strings.
- Assuming no embedded nulls:
strlenand pointer loops stop at the first null byte, which may truncate data if the string contains embedded nulls.
When to choose each approach
Choose index-based loops when you want to preserve the original pointer or need the index. Choose pointer traversal for concise code when you can afford to modify a local copy. Use length-based indexing only when you already need the length for other purposes and the string is short or performance is not critical. For read-only constant strings, prefer index or pointer loops that test the sentinel to avoid unnecessary passes.