Bytebook Compiler

Pointers & Memory

Pointers

Addresses, the & and * operators, pointer types, NULL, and pointer arithmetic — the idea that makes C powerful.

5 min read5 runnable examples4-question quiz

Every variable lives at some address in memory, like a house number. A pointer is a variable that stores an address. That one idea lets C build linked lists, pass big data without copying it, and talk directly to hardware.

& and *

  • &x — the address-of operator: "where does x live?"
  • *p — the dereference operator: "the value stored at the address in p".
C
#include <stdio.h>

int main(void) {
    int x = 10;
    int *p = &x;            // p is a pointer to int, holding x's address

    printf("x  = %d\n", x);
    printf("&x = %p\n", (void *)&x);
    printf("p  = %p (the same address)\n", (void *)p);
    printf("*p = %d (the value at that address)\n", *p);

    *p = 25;                // change x through the pointer
    printf("x is now %d\n", x);
    return 0;
}
Output · yours may differ
x  = 10
&x = 0x16f206768
p  = 0x16f206768 (the same address)
*p = 10 (the value at that address)
x is now 25

Addresses change every run, so your output will show different hex numbers. The picture below uses simple made-up addresses:

25xint0x10000x1000pint *0x2000
p stores the address of x, so *p and x are the same box of memory.

Declaring pointers

The type before * says what the pointer points to:

C
int    *ip;     // points to an int
double *dp;     // points to a double
char   *cp;     // points to a char
int *a, b;      // careful: a is a pointer, b is a plain int!

All pointers are the same size (8 bytes on a 64-bit machine) because they all hold an address. The type matters for dereferencing and arithmetic.

NULL and wild pointers

A pointer that has not been given an address yet should be set to NULL (address 0). Dereferencing NULL crashes the program immediately — which is much better than a wild pointer (uninitialised) that silently corrupts random memory.

C
#include <stdio.h>

int main(void) {
    int *p = NULL;
    if (p == NULL)
        printf("p points nowhere yet; do not dereference it.\n");
    int value = 42;
    p = &value;
    if (p != NULL)
        printf("Now *p = %d\n", *p);
    return 0;
}
Output
p points nowhere yet; do not dereference it.
Now *p = 42

Pointer arithmetic

Adding 1 to a pointer moves it to the next element, not the next byte. For an int * (4-byte ints), p + 1 is 4 bytes further on.

C
#include <stdio.h>

int main(void) {
    int a[5] = {10, 20, 30, 40, 50};
    int *p = a;                     // same as &a[0]

    printf("*p       = %d\n", *p);
    printf("*(p + 1) = %d\n", *(p + 1));
    printf("*(p + 4) = %d\n", *(p + 4));
    p++;                            // move to the next int
    printf("after p++, *p = %d\n", *p);
    printf("byte distance between &a[3] and &a[0] = %td\n", (char *)&a[3] - (char *)&a[0]);
    printf("element distance = %td\n", &a[3] - &a[0]);
    return 0;
}
Output
*p       = 10
*(p + 1) = 20
*(p + 4) = 50
after p++, *p = 20
byte distance between &a[3] and &a[0] = 12
element distance = 3
100201302403504pp+1p+4
p + k points k elements past p, whatever the element size.

Allowed pointer operations: add or subtract an integer, subtract two pointers into the same array, and compare pointers. You cannot add, multiply or divide two pointers.

Pointer to pointer

A pointer also lives at an address, so you can point to it:

C
#include <stdio.h>

int main(void) {
    int x = 7;
    int *p = &x;
    int **pp = &p;
    printf("x = %d, *p = %d, **pp = %d\n", x, *p, **pp);
    **pp = 99;
    printf("x after **pp = 99: %d\n", x);
    return 0;
}
Output
x = 7, *p = 7, **pp = 7
x after **pp = 99: 99
99xint0x10000x1000pint *0x20000x2000ppint **0x3000
pp points to p, which points to x.

void pointers

A void * can hold the address of any type, but you must cast it before dereferencing. malloc returns void *, which is why it can allocate memory for anything.

C
#include <stdio.h>

int main(void) {
    int n = 5;
    double d = 2.5;
    void *vp = &n;
    printf("int via void*: %d\n", *(int *)vp);
    vp = &d;
    printf("double via void*: %.1f\n", *(double *)vp);
    return 0;
}
Output
int via void*: 5
double via void*: 2.5

Test yourself

4 questions · pick an answer to check it instantly

  1. 1

    If int x = 5; int *p = &x;, what is *p?

  2. 2

    int a[] = {2, 4, 6, 8}; int *p = a; What is *(p + 2)?

  3. 3

    On a machine with 4-byte ints, if p (an int *) holds address 1000, what does p + 3 hold?

  4. 4

    What is a dangling pointer?