Pointers in C: A Beginner's Visual Guide
Pointers in C are variables that store the memory address of another variable instead of a normal value. They let you read and change data indirectly, share memory between functions, and work with arrays and dynamic memory. Once you picture a pointer as an arrow pointing to a labeled box in memory, the syntax becomes much easier to follow.
What a pointer really is
Every variable in your program lives at a numbered location in memory, called its address. Think of memory as a long street of houses, where each house has a number. A normal variable holds a value, like the number 25. A pointer holds a house number, the address where some value lives.
When we talk about pointers in C, we mean this idea exactly: a small variable that remembers "the data you want is over there." The pointer itself sits in its own house too, but the value it stores is directions to another house. That single concept drives everything else in this guide.
Addresses and the & operator
To get the address of any variable, put the & (address-of) operator in front of it. To print an address, use the %p format specifier.
#include <stdio.h>
int main(void) {
int age = 25;
printf("Value of age: %d\n", age);
printf("Address of age: %p\n", (void *)&age);
return 0;
}
The exact address you see will differ on every run and every machine, so never hard-code it. What matters is that &age gives you the location, while age gives you the stored value. We cast to (void *) because %p expects a generic pointer type.
Declaring a pointer
You declare a pointer by writing the type it points to, followed by a star. The line below creates a pointer to an int and stores the address of age inside it.
int age = 25;
int *p = &age; /* p now holds the address of age */
Read int *p as "p is a pointer to int." The star belongs to the variable, not the type, so if you write int *a, b; only a is a pointer and b is a plain int. Declaring each pointer on its own line avoids this beginner trap.
A pointer that does not yet point anywhere should be set to NULL. Checking for NULL before use prevents many crashes.
Dereferencing: reaching the value
The star has a second job. When you put it in front of an existing pointer, it means "go to that address and use the value there." This is called dereferencing.
#include <stdio.h>
int main(void) {
int age = 25;
int *p = &age;
printf("%d\n", *p); /* prints 25, the value at the address */
*p = 30; /* changes age through the pointer */
printf("%d\n", age); /* prints 30 */
return 0;
}
Notice that writing *p = 30; changed age itself, even though we never wrote age directly. The pointer gave us a second handle on the same box. This indirect access is exactly why pointers are powerful, and why functions can modify variables that live outside them.
Passing pointers to functions
C passes arguments by value, meaning a function gets a copy. If you pass a plain int, the function cannot change the caller's variable. Pass a pointer, and the function can reach back and edit the original.
#include <stdio.h>
void swap(int *x, int *y) {
int temp = *x;
*x = *y;
*y = temp;
}
int main(void) {
int a = 5, b = 9;
swap(&a, &b);
printf("a=%d b=%d\n", a, b); /* a=9 b=5 */
return 0;
}
Here swap receives the addresses of a and b, then uses dereferencing to exchange their values. Without pointers, this classic swap would be impossible in C.
Pointers and arrays
Arrays and pointers are close cousins. The name of an array, used by itself, decays into a pointer to its first element. So arr and &arr[0] mean the same address.
#include <stdio.h>
int main(void) {
int arr[3] = {10, 20, 30};
int *p = arr; /* same as &arr[0] */
printf("%d\n", *p); /* 10 */
printf("%d\n", *(p + 1)); /* 20 */
printf("%d\n", *(p + 2)); /* 30 */
return 0;
}
Adding 1 to a pointer does not add 1 byte. It moves forward by the size of one element, so p + 1 points to the next int. This is called pointer arithmetic, and it is why arr[i] is really shorthand for *(arr + i). Both index and pointer notation compile to the same thing.
Pointers to functions
A function also has an address, so you can store it in a pointer and call it later. This lets you choose behavior at runtime, which is handy for menus, callbacks, and sorting.
#include <stdio.h>
int add(int a, int b) { return a + b; }
int mul(int a, int b) { return a * b; }
int main(void) {
int (*op)(int, int); /* pointer to a function */
op = add;
printf("%d\n", op(2, 3)); /* 5 */
op = mul;
printf("%d\n", op(2, 3)); /* 6 */
return 0;
}
The declaration int (*op)(int, int) reads as "op is a pointer to a function taking two ints and returning an int." The parentheses around *op are required; without them you would declare a function returning a pointer instead.
Common mistakes to avoid
Pointers are strict, and small slips cause crashes or wrong results. Watch for these:
- Using an uninitialized pointer. A pointer that was never assigned holds garbage. Dereferencing it is undefined behavior. Set it to
NULLor a valid address first. - Dereferencing NULL. Writing
*pwhenpisNULLusually crashes the program. Checkif (p != NULL)before use. - Confusing the two stars. In a declaration,
int *pcreates a pointer. In an expression,*preads the value. Same symbol, different jobs. - Returning the address of a local variable. A local dies when its function ends, so the returned pointer dangles. Return by value or allocate on the heap instead.
- Forgetting that a copy is not the original. Passing a value cannot change the caller; pass a pointer when you need to modify it.
A quick practice idea
To make this concrete, try a tiny exercise on your own machine. Many students in Bangladesh start with the free gcc compiler on Linux or through an online compiler when a laptop is shared. Write a function void doubleIt(int *n) that doubles the value its argument points to, call it from main, and print the result. If the number changes in main after the call, your mental model of pointers is working.
Solid pointer skills carry directly into C++, embedded systems, and systems programming roles. If you want to put these fundamentals to work professionally, explore openings at Avian™ Career once you are comfortable writing and debugging your own pointer code.
Frequently asked questions
What is the difference between & and * in C?
The & operator gives you the address of a variable, so &age answers "where does this live?" The * operator, used on a pointer, gives you the value stored at that address. One asks for a location, the other follows a location to its contents.
Why does my program crash when I use a pointer?
The most common cause is dereferencing a pointer that is NULL or uninitialized, so it points to no valid memory. Always initialize pointers, set unused ones to NULL, and check if (p != NULL) before reading or writing through them.
Are arrays and pointers the same thing in C?
No, but they are closely related. An array name decays into a pointer to its first element in most expressions, so indexing and pointer arithmetic give identical results. However, an array reserves a fixed block of memory, while a pointer is a separate variable that can be reassigned.
Do I need pointers to learn C well?
Yes. Pointers are central to how C handles arrays, strings, function arguments, and dynamic memory. You can write small programs without them, but understanding pointers is required for real projects, file handling, data structures, and almost any job that uses C or C++.
Categories
- Tutorial
- Announcement
- News