Pointers and memory

Addresses, pointer arithmetic, where objects live, and the malloc and free rules that keep a program alive.

Addresses and indirection

A pointer holds an address. The two operators are & (address of) and * (dereference, the value at that address). The type of a pointer tells the compiler how far one step moves and how to read the bytes it finds.

int n = 42;
int *p = &n;          /* p points at n */
*p = 43;              /* writes through the pointer: n is now 43 */

int a[4] = { 10, 20, 30, 40 };
int *q = a;           /* an array name decays to a pointer to its first element */
q[2]  == *(q + 2)     /* 30: subscripting is addition plus a dereference */
&a[2] == a + 2        /* the same address, two spellings */

char s[] = "hello";   /* 6 bytes: 5 letters plus a terminating '' */
printf("%zu\n", strlen(s));   /* 5: the terminator is not counted */

void *raw = &n;       /* an untyped address, which must be cast before use */
int *back = (int *)raw;
Storage durationLifetimeTypical use
automaticUntil the enclosing block returnsLocals, allocated on the stack
staticThe whole program runstatic locals, globals, string literals
allocatedFrom malloc until freeAnything whose size is known only at run time

The first two are managed for you. The third is not: a pointer to automatic storage becomes invalid the moment the block ends, which is what makes return &local; a bug rather than a shortcut.

Allocating and freeing

#include <stdlib.h>
#include <string.h>

size_t n = 1000;
int *buf = malloc(n * sizeof *buf);   /* sizeof *buf, not sizeof(int) */
if (buf == NULL) {
    return EXIT_FAILURE;              /* allocation failed: nothing to free */
}
memset(buf, 0, n * sizeof *buf);
/* ... use buf ... */
free(buf);
buf = NULL;                           /* makes a later double free harmless */
  • malloc returns uninitialised memory, calloc zeroes it, and realloc resizes a block, possibly moving it.
  • Every successful allocation needs exactly one free, and only for a pointer returned by an allocator.
  • Never use a pointer after freeing it, never free it twice, and never free the middle of a block.
  • free(NULL) is defined and does nothing, but a NULL check is still required on the result of malloc.
  • Writing p = malloc(...) into an existing pointer leaks the old block: free it first, or keep both pointers until the new allocation succeeds.
/* the realloc trap: assigning straight back loses the old block on failure */
int *grown = realloc(buf, 2000 * sizeof *buf);
if (grown == NULL) {
    free(buf);                  /* buf is still valid and still yours to free */
    return EXIT_FAILURE;
}
buf = grown;

Strings and buffers

A C string is an array of char ending in '\0'. Its length is not stored anywhere, so every function that copies one needs to be told how much room there is.

char dst[8];

strcpy(dst, "123456789");            /* writes 10 bytes into 8: undefined behaviour */

strncpy(dst, src, sizeof dst - 1);   /* bounded, but may leave no terminator */
dst[sizeof dst - 1] = '\0';          /* so terminate it yourself */

snprintf(dst, sizeof dst, "%s-%d", src, 7);   /* truncates safely, always terminates */

/* strlen walks bytes until the terminator, so it needs a terminated buffer */
size_t len = strlen(dst);
⚠️
A buffer overflow is not a crash you can rely on noticing. It silently corrupts whatever memory follows and is the classic route to remote code execution. Use the n variants with an explicit size, check every length that comes from outside the program, and run memory-sensitive code under -fsanitize=address.

FAQ

What is the difference between a pointer and an array?
An array is a block of storage; a pointer is a value holding an address. In most expressions an array name converts to a pointer to its first element, but sizeof a gives the size of the whole array while sizeof p gives the size of a pointer.
When should a function return a pointer?
Only for static storage, heap memory the caller will free, or a buffer the caller passed in. Returning the address of a local variable hands back a dangling pointer the moment the function returns.

Syntax and the compiled toolchain Structs, files and undefined behaviour

Last refreshed 2026-09-18.