Lesson 8 of 12
Pointers
Learn what a is: a that holds a memory address. , , structs passed to and dynamic memory all rely on the same pointer rules.
#include <stdio.h>
int main(void) {
int x = 42;
int *p = &x; /* & takes the address of x */
printf("value: %d\n", x); /* 42 */
printf("address: %p\n", (void *)p);
printf("deref: %d\n", *p); /* 42 — * reads through the pointer */
*p = 99; /* write through the pointer */
printf("x is now %d\n", x); /* 99 */
int *nothing = NULL; /* always initialise; NULL means "points nowhere" */
if (nothing != NULL) printf("%d\n", *nothing); /* check before dereferencing */
/* Declaration syntax reads right to left */
int a = 1;
int *ptr_to_int = &a;
int **ptr_to_ptr = &ptr_to_int;
printf("%d\n", **ptr_to_ptr); /* 1 */
const int *read_only = &a; /* cannot write *read_only */
int *const fixed_target = &a; /* cannot change where it points */
return 0;
}#include <stdio.h>
#include <string.h>
/* C passes everything BY VALUE. To let a function modify a caller's variable,
pass its address. */
void broken_swap(int a, int b) { int t = a; a = b; b = t; } /* does nothing */
void swap(int *a, int *b) { int t = *a; *a = *b; *b = t; } /* works */
/* Returning several values: write them through out-parameters */
int divide(int num, int den, int *quotient, int *remainder) {
if (den == 0) return -1; /* error code */
*quotient = num / den;
*remainder = num % den;
return 0;
}
/* Arrays decay to a pointer to their first element when passed,
so the length must travel separately. */
int sum(const int *values, size_t count) {
int total = 0;
for (size_t i = 0; i < count; i++) total += values[i]; /* same as *(values + i) */
return total;
}
int main(void) {
int a = 1, b = 2;
swap(&a, &b);
printf("%d %d\n", a, b); /* 2 1 */
int q, r;
if (divide(17, 5, &q, &r) == 0) printf("%d rem %d\n", q, r); /* 3 rem 2 */
int nums[] = {1, 2, 3, 4, 5};
printf("%d\n", sum(nums, sizeof nums / sizeof nums[0])); /* 15 */
return 0;
}#include <stdio.h>
#include <stdlib.h>
/* Pointer arithmetic moves in units of the pointed-to TYPE, not bytes. */
void walk(void) {
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr;
printf("%d\n", *p); /* 10 */
printf("%d\n", *(p + 2)); /* 30 — advanced 2 ints, i.e. 8 bytes */
p++;
printf("%d\n", *p); /* 20 */
printf("%td\n", p - arr); /* 1 — distance in elements */
for (int *it = arr; it != arr + 5; ++it) printf("%d ", *it);
printf("\n");
}
/* Function pointers: pass behaviour as a parameter */
int ascending(const void *a, const void *b) { return *(const int *)a - *(const int *)b; }
int descending(const void *a, const void *b) { return *(const int *)b - *(const int *)a; }
int main(void) {
walk();
int data[] = {5, 2, 9, 1};
size_t n = sizeof data / sizeof data[0];
qsort(data, n, sizeof data[0], ascending);
for (size_t i = 0; i < n; i++) printf("%d ", data[i]); /* 1 2 5 9 */
printf("\n");
int (*compare)(const void *, const void *) = descending;
qsort(data, n, sizeof data[0], compare);
for (size_t i = 0; i < n; i++) printf("%d ", data[i]); /* 9 5 2 1 */
printf("\n");
return 0;
}Common question
Is a pointer a hard idea?
Memory is a numbered array of . A pointer is a variable whose value is one of those numbers, plus a type saying how to read what is stored there. & takes an address, * follows one, and pointer arithmetic moves in steps the size of the pointed-to type. Everything else (arrays, strings, passing structs cheaply, dynamic allocation, linked structures) is built on that.
The difficulty is in the habits. Initialise every pointer, never dereference one you have not checked, know who owns each allocation and free it exactly once, and pointers to after freeing. Tools like a sanitiser or Valgrind catch what you miss.
Source: Kernighan and Ritchie, The C Programming Language: "Pointers have been lumped with the goto statement as a marvelous way to create impossible-to-understand programs. This is certainly true when they are used carelessly."
Common mix-ups
Each of these sounds reasonable. Think about why it is wrong, then open the explanation.
Pointers are famously hard, so the idea behind them must be difficult.
An array is a pointer, since both can be indexed and passed to functions.
Passing a pointer to a function lets the function change the pointer itself.
Tip: use the ← and → keys to move between lessons.