Turning Learners Into Developers
Codekilla
CODEKILLA
C Language 7 min

Pointers in C: The Mental Model That Finally Makes Them Click

A pointer is a variable that stores a memory address instead of a regular value like a number or character. Think of it like this: your house number isn't your house—it's just a way to find your house.

Rahul Chaudhary Fri Oct 09 2026
What is a Pointer?

A pointer is a variable that stores a memory address instead of a regular value like a number or character. Think of it like this: your house number isn't your house—it's just a way to find your house. Similarly, a pointer doesn't contain data directly; it contains the location where that data lives in your computer's memory.

When you declare int *ptr, you're creating a variable that can hold the address of an integer. The * symbol means "this is a pointer," and when you use it later in your code, it means "go to that address and grab what's there." This dual meaning trips up beginners, but once you separate "declaring a pointer" from "dereferencing a pointer," everything clicks.

Why It Matters
  • Dynamic memory allocation — You can't build data structures like linked lists, trees, or custom-sized arrays without pointers managing heap memory.
  • Passing by reference — Functions in C can only modify variables outside their scope if you pass pointers, enabling real-world programs where functions actually change data.
  • Low-level hardware control — Embedded systems, device drivers, and operating systems rely on pointers to directly manipulate memory addresses and hardware registers.
  • Efficiency — Passing a pointer to a large struct takes 8 bytes (on 64-bit systems), while copying the whole struct might take thousands of bytes.
  • Understanding other languages — Even though languages like Python hide pointers, they still use them internally. Knowing C pointers demystifies references, object IDs, and memory leaks everywhere else.
The Address-Of and Dereference Operators

Every pointer operation boils down to two symbols: & (address-of) and * (dereference). The & operator answers "where does this variable live?" while * answers "what's stored at this address?"

Here's the mental model: imagine memory as a street of houses. Each house (memory location) has a number (address) and contains furniture (data). The & operator gives you the house number. The * operator opens the door and shows you what's inside.

c
#include <stdio.h>

int main() {
    int score = 42;
    int *ptr = &score;  // ptr now holds the address of score
    
    printf("Value of score: %d\n", score);           // 42
    printf("Address of score: %p\n", (void*)&score); // e.g., 0x7ffd5c...
    printf("Value of ptr: %p\n", (void*)ptr);        // same address
    printf("Value at address ptr: %d\n", *ptr);      // 42
    
    *ptr = 100;  // change the value at that address
    printf("New value of score: %d\n", score);       // 100
    
    return 0;
}

Notice how changing *ptr changed score—they're pointing to the same memory location. This is the core power of pointers.

Pointer Types Matter

You might wonder why we declare int *ptr instead of just void *ptr for everything. The type tells the compiler how many bytes to read when you dereference. An int is typically 4 bytes, a char is 1 byte, and a double is 8 bytes.

TypeBytes ReadUse Case
char *1Strings, single bytes
int *4Integer arrays, counters
double *8Floating-point calculations
void *undefinedGeneric pointers (cast later)

When you write ptr + 1, C doesn't add 1 byte—it adds the size of whatever type ptr points to. This is called pointer arithmetic, and it's why iterating through an array with pointers works seamlessly.

c
#include <stdio.h>

int main() {
    int numbers[] = {10, 20, 30, 40};
    int *ptr = numbers;  // arrays decay to pointers
    
    for (int i = 0; i < 4; i++) {
        printf("numbers[%d] = %d, *(ptr + %d) = %d\n", 
               i, numbers[i], i, *(ptr + i));
    }
    
    return 0;
}

The expression *(ptr + i) is identical to numbers[i]—arrays and pointers are deeply intertwined in C.

Pointers vs Arrays: The Confusion

Arrays and pointers look similar, but they're not the same. An array name decays into a pointer to its first element in most contexts, yet you can't reassign an array name like you can a pointer.

FeatureArray int arr[5]Pointer int *ptr
StorageAllocates space for dataOnly stores an address
ReassignableNo (fixed)Yes (ptr = &other)
sizeof()Total array sizeSize of pointer (8 bytes)
Decay to pointerYes, in expressionsAlready a pointer

Think of an array as a permanent house you own, while a pointer is a piece of paper where you can write different addresses. You can point a pointer at an array, but you can't make the array name point somewhere else.

c
#include <stdio.h>

int main() {
    int arr[3] = {1, 2, 3};
    int *ptr = arr;  // valid: ptr points to arr[0]
    
    printf("arr size: %zu bytes\n", sizeof(arr));   // 12 bytes (3 * 4)
    printf("ptr size: %zu bytes\n", sizeof(ptr));   // 8 bytes (pointer itself)
    
    ptr = ptr + 1;  // valid: now points to arr[1]
    // arr = arr + 1;  // ERROR: can't reassign array name
    
    return 0;
}
Dynamic Memory: malloc and free

Stack memory (local variables) vanishes when a function returns. To keep data alive longer, you allocate on the heap using malloc, and you must call free when done, or you leak memory.

c
#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int*)malloc(5 * sizeof(int));  // allocate space for 5 ints
    
    if (ptr == NULL) {
        printf("Memory allocation failed!\n");
        return 1;
    }
    
    for (int i = 0; i < 5; i++) {
        ptr[i] = i * 10;  // treat like an array
    }
    
    for (int i = 0; i < 5; i++) {
        printf("%d ", ptr[i]);
    }
    printf("\n");
    
    free(ptr);  // release memory back to the system
    ptr = NULL; // good practice: avoid dangling pointers
    
    return 0;
}

The pattern is: allocate, check for NULL, use, free. Miss any step and you'll either crash or leak memory. Modern tools like Valgrind help catch these mistakes.

Passing Pointers to Functions

C passes arguments by value, meaning functions get copies of variables. To modify the original variable, you pass its address as a pointer.

c
#include <stdio.h>

void increment_wrong(int x) {
    x = x + 1;  // modifies the copy, not the original
}

void increment_right(int *x) {
    *x = *x + 1;  // modifies the value at the address
}

int main() {
    int num = 5;
    
    increment_wrong(num);
    printf("After increment_wrong: %d\n", num);  // still 5
    
    increment_right(&num);
    printf("After increment_right: %d\n", num);  // now 6
    
    return 0;
}

This is why scanf needs &variable—it must write directly to your variable's memory location, not a copy.

Quick Cheat Sheet
NeedReach for
Get address of a variable&variable
Access value at an address*pointer
Declare a pointerint *ptr;
Allocate memory on the heapmalloc(size)
Free heap memoryfree(ptr)
Modify a function's argumentPass &variable
Iterate through an arrayPointer arithmetic ptr++
Avoid dereferencing NULL/freed memoryCheck if (ptr != NULL)
Common Mistakes
  • Dereferencing uninitialized pointers — int *ptr; *ptr = 5; writes to a random address and crashes. Always initialize with NULL or a valid address.
  • Forgetting to free malloc'd memory — Every malloc needs a matching free, or your program leaks memory until it runs out.
  • Using a pointer after free (dangling pointer) — Set pointers to NULL after freeing to catch accidental re-use.
  • Confusing * in declarations vs expressions — int *ptr declares a pointer, while *ptr dereferences it. They look the same but mean different things.
  • Returning pointers to local variables — Local variables die when the function returns, leaving you with a pointer to garbage.
  • Off-by-one errors in pointer arithmetic — ptr + n moves n * sizeof(type) bytes, not n bytes. Easy to overshoot array bounds.

💡 Think Like a Programmer: Every pointer is either pointing to valid memory, NULL, or disaster. Make it a habit to ask "where does this point?" before dereferencing, and pointers become your most powerful tool instead of your biggest headache.

// was this useful?
Did this article answer your question?
// C Language · published by Codekilla
// related articles

Keep Reading