11. Pointers to pointers, function pointers, pointers to arrays
11.1 Pointers to pointers: modifying a pointer through a function
#include <stdio.h>
#include <stdlib.h>
int allocate(int **out, int value) {
*out = malloc(sizeof(int)); // to change the CALLER's pointer, you need its address
if (!*out) return 0;
**out = value;
return 1;
}
int main(void) {
int *p = NULL;
if (!allocate(&p, 42)) { perror("malloc"); return 1; }
printf("%d\n", *p);
free(p);
return 0;
}
This is also exactly how argv works: int main(int argc, char *argv[])
— argv is char **, an array of pointers to char (strings), because
main needs a variable number of variable-length strings, which is
precisely what a pointer-to-pointer expresses.
11.2 Function pointers: C's mechanism for callbacks and polymorphism
#include <stdio.h>
#include <stdlib.h>
int compare_asc(const void *a, const void *b) {
int ia = *(const int *)a, ib = *(const int *)b;
return (ia > ib) - (ia < ib);
}
int compare_desc(const void *a, const void *b) {
int ia = *(const int *)a, ib = *(const int *)b;
return (ib > ia) - (ib < ia);
}
int main(void) {
int nums[] = {5, 2, 8, 1, 9};
int n = sizeof(nums) / sizeof(nums[0]);
int (*cmp)(const void *, const void *) = compare_asc; // a variable holding a function
qsort(nums, (size_t)n, sizeof(int), cmp);
for (int i = 0; i < n; i++) printf("%d ", nums[i]);
printf("\n");
qsort(nums, (size_t)n, sizeof(int), compare_desc);
for (int i = 0; i < n; i++) printf("%d ", nums[i]);
printf("\n");
return 0;
}
qsort's comparator argument is the same mechanism used for every
callback-driven C API you'll meet in Part VI — curl's
CURLOPT_WRITEFUNCTION, Redis's command dispatch table, and every event
loop's "call this function when X happens."
A typedef makes function-pointer types readable:
typedef int (*Comparator)(const void *, const void *);
void sort_ints(int *arr, size_t n, Comparator cmp) {
qsort(arr, n, sizeof(int), cmp);
}
11.3 Pointers to arrays vs. arrays of pointers — the syntax that trips everyone up
int (*p1)[5]; // p1 is a pointer to an array of 5 ints
int *p2[5]; // p2 is an array of 5 pointers to int
int matrix[3][4];
int (*row)[4] = matrix; // row points to a whole 4-int row; row+1 skips 4 ints
Read declarations from the variable name outward, following precedence:
[] and () bind tighter than *, so int *p2[5] groups as
int *(p2[5]) — an array, of pointers.