Pointers to anything Ready
x, a pointer to x, and a pointer to that pointer.
main.c

                

Ready

Statements run 0 Lines printed 0
Ready

Keyboard: ← → step · Space play / pause · R reset · Esc exit classroom mode

How it works

The ideas behind the lab

Exactly what you watched happen, in plain steps.

Anything with an address can be pointed to

A pointer is itself a variable, so it has an address — and a pointer to it is a pointer to a pointer (int **). An array can hold pointers, which is the usual way to keep a list of strings of different lengths. Functions also live at addresses, so a function pointer can store which function to call. const decides whether the pointed-to data, the pointer, or both may change, and void * can hold any address but must be cast before use.

int x = 5, *p = &x, **pp = &p;   // **pp is x

const char *names[] = {"Ann", "Bob"};   // array of pointers

int add(int a, int b);
int (*op)(int, int) = add;   // function pointer
op(2, 3);                    // calls add: 5

void *any = &x;              // any address
int y = *(int *) any;        // cast before use
const and pointers — read the declaration from right to left
Declaration Change *p? Change p? Read as
int *p yes yes p is a pointer to int
const int *p no yes p is a pointer to a const int
int *const p yes no p is a const pointer to int
const int *const p no no p is a const pointer to a const int
void *p only after a cast yes p points to "something"

Pointers to pointers

  1. int **pp = &p; — pp holds the address of the pointer p.
  2. *pp is p (an address); **pp is the int p points to.
  3. A function that must change the caller's pointer takes a pointer to it: void f(int **pp).
  4. char **argv in main is a pointer to an array of string pointers.
  5. Each extra * is one more hop to follow.

Arrays of pointers

  1. const char *names[3] holds 3 addresses (24 bytes), not the text.
  2. Each string can have a different length — no wasted space.
  3. names[i] is a string; names[i][j] is one character of it.
  4. Swapping or sorting moves only the 8-byte pointers.
  5. Compare char grid[3][10]: fixed-size rows that are copied to swap.

Function pointers and void *

  1. int (*f)(int) — the brackets make f a pointer; int *f(int) would be a function returning int *.
  2. Assign a function name: f = square; call it: f(5).
  3. Pass functions to functions ("callbacks"), e.g. qsort's compare function.
  4. void * holds any data address; malloc returns one (Module 33).
  5. You cannot dereference a void * or do arithmetic on it — cast it first.
Walkthrough

Callbacks, an array of strings, ** and void *

Every line, explained.

1 #include <stdio.h>
2
3 int twice(int n) { return 2 * n; }
4 int square(int n) { return n * n; }
5
6 void applyAll(int a[], int n, int (*f)(int)) f: any function taking int and returning int.
7 {
8 for (int i = 0; i < n; i++)
9 a[i] = f(a[i]); Calls whatever function was passed in.
10 }
11
12 void setToNull(int **pp) Receives the address of a pointer.
13 {
14 *pp = NULL; Changes the caller's pointer.
15 }
16
17 int main(void)
18 {
19 int v[] = {1, 2, 3};
20 applyAll(v, 3, twice); v = 2 4 6
21 applyAll(v, 3, square); v = 4 16 36
22 printf("%d %d %d\n", v[0], v[1], v[2]); Prints 4 16 36
23 const char *days[] = {"Mon", "Tue", "Wed"};
24 printf("%s %c\n", days[1], days[2][0]); Prints Tue W
25 int x = 1;
26 int *p = &x;
27 setToNull(&p);
28 printf("%s\n", p == NULL ? "p is NULL" : "p is set"); Prints p is NULL
29 void *any = &x; A void * may hold any address …
30 printf("%d\n", *(int *) any); … but must be cast before use: prints 1
31 return 0;
32 }
Common mistakes

Spot the bug

Mistakes almost every beginner makes — and how to fix them.

Missing brackets in a function pointer

✗ Wrong
int *op(int, int);     // a function returning int *
✓ Right
int (*op)(int, int);   // a pointer to a function

( ) binds tighter than *, so the brackets around *op are essential.

Changing a pointer parameter instead of the caller's pointer

✗ Wrong
void clear(int *p) { p = NULL; }   // caller unchanged
✓ Right
void clear(int **pp) { *pp = NULL; }
clear(&p);

To change the caller's pointer, pass its address — a pointer to the pointer.

Dereferencing a void *

✗ Wrong
void *v = &x;
printf("%d", *v);   // error
✓ Right
void *v = &x;
printf("%d", *(int *) v);

The compiler does not know what type is stored there; tell it with a cast.

Writing through a pointer to const

✗ Wrong
const int *p = &x;
*p = 5;   // error
✓ Right
int *p = &x;
*p = 5;

const int * promises not to change the data through p.

Changing a string through an array of literal pointers

✗ Wrong
char *names[] = {"Ann"};
names[0][0] = 'a';   // crash
✓ Right
char names[][4] = {"Ann"};
names[0][0] = 'a';

The pointers point at string literals in read-only memory. Use const char * to make the compiler catch it.

Practice

Check your understanding

Answer each question — you will see the explanation straight away.

0 / 5 correct
Question 1

int x = 3; int *p = &x; int **pp = &p; — what is **pp?

Question 2

const char *n[] = {"Ann", "Bob"}; — what does n[1][0] give?

Question 3

Which declares a pointer to a function taking an int and returning an int?

Question 4

const int *p = &x; — which is allowed?

Question 5

What must you do before using the value behind a void * ?

Assessment

More Pointers quiz

Test yourself: 10 questions, 70% to pass. Create a free account to take the quiz, save your progress and work towards the certificate.