Pointers & Memory
Pointers
Addresses, the & and * operators, pointer types, NULL, and pointer arithmetic — the idea that makes C powerful.
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 doesxlive?"*p— the dereference operator: "the value stored at the address inp".
#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;
}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:
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:
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.
#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;
}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.
#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;
}*p = 10 *(p + 1) = 20 *(p + 4) = 50 after p++, *p = 20 byte distance between &a[3] and &a[0] = 12 element distance = 3
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:
#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;
}x = 7, *p = 7, **pp = 7 x after **pp = 99: 99
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.
#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;
}int via void*: 5 double via void*: 2.5
Test yourself
4 questions · pick an answer to check it instantly
- 1
If
int x = 5; int *p = &x;, what is*p?*pdereferences the pointer, giving the value stored atx's address. - 2
int a[] = {2, 4, 6, 8}; int *p = a;What is*(p + 2)?p + 2points two elements ahead, ata[2], which is 6. - 3
On a machine with 4-byte ints, if
p(anint *) holds address 1000, what doesp + 3hold?Pointer arithmetic scales by the element size: 1000 + 3 × 4 = 1012. - 4
What is a dangling pointer?
The pointer still holds an address, but the memory there no longer belongs to you.