C in 100 Seconds: Arrays of Pointers | Episode 28
Video: C in 100 Seconds: Arrays of Pointers — String Lists and Pointer Tables | Episode 28 by Taught by Celeste AI - AI Coding Coach
C Arrays of Pointers: String Lists and Pointer Tables
char *colors[] = {"red", "green", "blue"};— array of pointers to strings. Each element is a pointer; each pointer points at a string literal. The standard way to hold a list of strings.
C doesn't have a "list of strings" type. The equivalent is an array of char * — pointers to the actual string data.
The basic shape
#include <stdio.h>
int main() {
char *colors[] = {"red", "green", "blue"};
int len = sizeof(colors) / sizeof(colors[0]);
for (int i = 0; i < len; i++) {
printf("colors[%d] = %s\n", i, colors[i]);
}
int a = 10, b = 20, c = 30;
int *nums[] = {&a, &b, &c};
for (int i = 0; i < 3; i++) {
printf("*nums[%d] = %d\n", i, *nums[i]);
}
return 0;
}
char *[] vs char [][]
Two ways to hold multiple strings:
// Option A: array of pointers
char *names[] = {"Alice", "Bob", "Charlie"};
// Option B: 2D array of chars
char names[3][20] = {"Alice", "Bob", "Charlie"};
| Aspect | char *[] |
char [][] |
|---|---|---|
| Memory | Pointers + read-only literals | Fixed buffer per slot |
| Modifiable? | No (literals are read-only) | Yes |
| Per-string size | Variable | Fixed (must fit longest) |
| Memory used | Smallest possible | Worst case × N |
For constant strings — menu items, error messages — char *[] is cleaner. For modifiable strings, char [N] (fixed-size) or dynamically allocated char ** (heap of strings).
Iterating
for (int i = 0; i < len; i++) {
printf("%s\n", colors[i]);
}
colors[i] is the i-th char *. printf("%s", ...) follows the pointer to print the string.
To iterate while looking for a sentinel:
char *args[] = {"a", "b", "c", NULL};
for (int i = 0; args[i] != NULL; i++) {
printf("%s\n", args[i]);
}
NULL-terminated arrays are common (mimicking how strings end with \0). The classic example is char **argv in main — terminated with argv[argc] == NULL.
Pointer to a non-string
int a = 10, b = 20, c = 30;
int *nums[] = {&a, &b, &c};
for (int i = 0; i < 3; i++) {
printf("*nums[%d] = %d\n", i, *nums[i]);
}
int *nums[] is "array of pointers to int." Each element points to a different int. Useful when the ints are scattered in memory or have different lifetimes.
For a contiguous array of ints, just use int nums[3] — no pointers needed.
String literals are read-only
char *colors[] = {"red", "green", "blue"};
colors[0][0] = 'R'; // UNDEFINED — string literals are in read-only memory
Each "red" is a string literal — the compiler stores it in a read-only section. Modifying it crashes (or worse, silently does nothing).
For modifiable strings:
char colors_storage[3][10] = {"red", "green", "blue"};
char *colors[3];
for (int i = 0; i < 3; i++) {
colors[i] = colors_storage[i];
}
colors[0][0] = 'R'; // OK now
Or heap-allocate each:
char *colors[3];
colors[0] = strdup("red");
colors[1] = strdup("green");
colors[2] = strdup("blue");
// remember to free each
for (int i = 0; i < 3; i++) free(colors[i]);
char **argv in main
int main(int argc, char *argv[]) {
for (int i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
}
argv is an array of pointers to strings — the command-line arguments. argv[0] is the program name; argv[1...argc-1] are the args; argv[argc] is NULL.
The signature char *argv[] and char **argv are equivalent (arrays decay to pointers).
Passing to functions
void print_strings(char *strs[], int n) {
for (int i = 0; i < n; i++) {
printf("%s\n", strs[i]);
}
}
print_strings(colors, 3);
char *strs[] is the parameter — equivalent to char **strs. The array decays to a pointer-to-pointer.
For variable-length lists (NULL-terminated):
void print_strings_null(char *strs[]) {
for (int i = 0; strs[i] != NULL; i++) {
printf("%s\n", strs[i]);
}
}
No length parameter needed.
Sorting
#include <stdlib.h>
#include <string.h>
int strcmp_ptr(const void *a, const void *b) {
return strcmp(*(const char **)a, *(const char **)b);
}
char *colors[] = {"red", "green", "blue"};
qsort(colors, 3, sizeof(char *), strcmp_ptr);
// colors is now {"blue", "green", "red"}
qsort works on any array; you supply the comparator. qsort passes pointers to the elements — for an array of char *, that's char **. The comparator dereferences and calls strcmp.
Common mistakes
Modifying string literals. colors[0][0] = 'R'; — undefined when the strings are literals. Use modifiable storage.
Confusing char *[] and char [][]. Different memory layouts. Different mutability.
sizeof after passing to a function. sizeof(strs) inside the function is pointer size, not array bytes. Pass length separately.
NULL not-checked in NULL-terminated lists. for (int i = 0; ; i++) printf("%s", strs[i]); runs forever past the terminator.
strdup without freeing. strdup malloc's a copy. Each string needs its own free.
What's next
Episode 29: double pointers. int **pp — pointer to pointer. The mechanism behind char **argv and dynamically-resizable arrays of pointers.
Recap
char *names[] for an array of pointers to strings — read-only literals are typical. Modify? Use char names[N][M] or heap-allocated char **. Iterate by index or NULL-terminator. argv in main is an array of char *. For sorting, qsort with a comparator that dereferences. String literals are read-only — never modify them.
Next episode: double pointers.