Pointers

A pointer is a variable that stores a memory address instead of an ordinary value — the location of another variable, rather than the variable itself. They're the mechanism behind everything from function arguments that can modify a caller's data to arrays, strings, and dynamic memory.

Address-of and dereference

& in front of a variable gives you its address. * in front of a pointer gives you back the value stored at the address it holds:

C main.c
#include <stdio.h>

int main(void) {
    int age = 30;
    int *ptr = &age;

    printf("age = %d\n", age);
    printf("&age = %p\n", (void *)&age);
    printf("ptr = %p\n", (void *)ptr);
    printf("*ptr = %d\n", *ptr);

    return 0;
}
Output
age = 30
&age = 0x7ffd3a2c0a3c
ptr = 0x7ffd3a2c0a3c
*ptr = 30

int *ptr declares ptr as a pointer to an int. &age gives its address, which is what gets stored in ptr — so ptr and &age print the same value. *ptr follows that address back to the int sitting there, giving 30. The exact address will differ every time you run the program; only the relationship matters.

Modifying a caller's variable through a pointer

C always passes arguments by value — a function gets its own copy and can't change the original. Passing a pointer instead lets the function reach back and modify whatever the pointer points at:

C main.c
#include <stdio.h>

void doubleIt(int *n) {
    *n = *n * 2;
}

int main(void) {
    int value = 21;

    doubleIt(&value);
    printf("value = %d\n", value);

    return 0;
}
Output
value = 42

Passing a plain int to doubleIt would only double a throwaway copy. Passing &value hands the function value's actual address, so *n = *n * 2 reaches back into main's memory and changes value itself.

Pointer arithmetic and arrays

An array's name, used on its own, decays into a pointer to its first element — which is why pointers and arrays step through memory the same way:

C main.c
#include <stdio.h>

int main(void) {
    int scores[3] = {10, 20, 30};
    int *p = scores;

    printf("%d %d %d\n", *p, *(p + 1), *(p + 2));

    return 0;
}
Output
10 20 30

p + 1 doesn't add one byte — it adds one sizeof(int), so it lands on the next element, not the next byte. That's why scores[i] and *(scores + i) mean exactly the same thing to the compiler.

Note: a pointer that hasn't been set to a valid address — an uninitialized local, or one left over after its target goes away — is called a wild or dangling pointer. Dereferencing it is undefined behavior: it might crash immediately, or it might silently corrupt unrelated memory and crash somewhere else entirely, much later. Always initialize a pointer to a real address, or explicitly to NULL if it doesn't have one yet, and check for NULL before dereferencing.