Arrays & Strings · beginner · ~10 min

C strings

Use the convention: char* + NUL terminator + length-by-scan.

Overview

A C string is just an array of char terminated by a NUL byte ('\0'). There is no string object: every string operation in C is array math with a watchful eye on the terminator. strlen walks until it sees '\0'; printf("%s", s) does the same.

Why it matters

Strings are the universal text format. URLs, filenames, HTTP headers, log lines, JSON, source code — all strings. C's bare-bones model means you can be extremely fast (no allocation, no Unicode unless you ask for it) but you must do every length and overflow check yourself. Half of all C CVEs trace back to string mistakes.

Core concepts

NUL terminator. The single byte '\0' marks the end of a string. Skip it and strlen will walk into uninitialized memory. Length vs capacity. strlen(s) is the number of bytes before the NUL; the buffer's capacity must be at least strlen(s) + 1. String literals. "hello" is a 6-byte read-only array (5 chars + NUL). Pointing to it with char *p = "hello"; and then doing p[0] = 'H'; is undefined behaviour.

Syntax notes

char s[] = "hello";       // mutable: 6 bytes on the stack
char *p  = "hello";       // read-only literal, don't write through p
size_t n = strlen(s);       // 5  (not counting the NUL)
char buf[64];
strcpy(buf, s);             // unsafe if s is too long
snprintf(buf, sizeof buf, "%s", s);  // safe — truncates if too long

Lesson

A C string is a char * pointing at a sequence of bytes ending in \0. The standard library functions in <string.h> operate on this convention: strlen walks to the NUL; strcpy copies until and including the NUL.

Because the length isn't stored, every operation that needs it is O(n). Code that calls strlen in a loop condition is the canonical O(n²) anti-pattern.

Code examples

#include <string.h>
const char *s = "hello";
size_t n = strlen(s);  // 5 — not 6

Common mistakes

  • strlen on non-NUL-terminated data reads past the end.
  • Calling strlen inside a loop condition: for (i=0; i<strlen(s); i++) — strlen is O(n), the loop becomes O(n²).

Debugging tips

When strlen or printf produces garbage, your buffer is missing the NUL terminator — printf("%.10s", s) limits the length so you can survive long enough to debug. Use gdb's x/32cb s to dump raw bytes when you suspect corruption.

Memory safety

strcpy, strcat, gets, and sprintf are unbounded — they keep writing until they hit a NUL in the source, even if your destination is too small. Replace them with strlcpy, strlcat (BSD), or snprintf everywhere. A 'safe' way to remember: anywhere you would use strcpy, the destination must be at least strlen(src) + 1 bytes — if you can't prove that, use a bounded version.

Real-world uses

HTTP parsing, JSON encoding, log line generation, filename construction, password validation, every command-line tool that takes textual input.

Practice tasks

  1. Write size_t my_strlen(const char *). 2. Write char *my_strcpy(char *dst, const char *src). 3. Write a function that uppercases a string in place. 4. Use snprintf to safely build "User: %s (%d)" into a 64-byte buffer.

Summary

C strings are arrays + NUL. Track the length yourself, never use unbounded copy functions, and remember that a string literal is read-only. Treat every string operation as a bounds check you write yourself, because the language won't.

Practice with these exercises