int x = 42;Pointers in C: Addresses, References, Types, and Practical Uses
What is Pointers in C? Core Concept & Direct Summary
What is a pointer in C, how does it store physical memory addresses, and how do you declare and initialize pointers?
Pointers have a reputation. Ask around and you'll hear that they're the hard part of C, the thing that separates people who "get it" from people who don't, the source of segfaults and mysterious crashes.
Here's the truth: pointers are conceptually simple — they're just variables that hold addresses instead of values. The difficulty isn't the idea; it's that pointers require you to think about something most programming languages hide from you entirely: where things live in memory.
That's the real hurdle. You have to build a mental picture of memory as a numbered grid of boxes, and of a pointer as a box containing another box's number. Once that picture is solid, pointers become mundane — and enormously powerful.
So that's what this page does: builds the picture first, then the syntax, then the uses. No magic, no hand-waving.
(Suggested image: the hero graphic — a memory strip with numbered cells; box at address 1004 contains the value 1000, and an arrow from it points to the box at address 1000 which contains 42. Label the first "pointer ptr" and the second "variable x". This single image is the whole concept.)
How Does Pointers in C Work? In-Depth Mechanics & Architecture
Step 1: Every Variable Lives Somewhere
Start with a plain variable:
That 42 isn't floating in the abstract — it's stored in a specific byte location in your computer's memory. You can ask C for that location with the address-of operator, &:
Sample output:
That hex number is the address — where x lives. (The exact value varies every run; that's normal. Stack addresses change with ASLR and environment.)
Now the key insight: addresses are just numbers. And numbers can be stored in variables. A variable that stores an address is called a pointer.
(Suggested image: a single labeled box — "x: 42" at address 0x7ffd... — establishing that variables have locations)
Step 2: Declaring a Pointer
To store the address of an int, you need a pointer-to-int:
Read the declaration with the * attached to the name, not the type: "ptr is a pointer to int." A common style is int* ptr; — same meaning, different emphasis. Both compile; pick one and be consistent. (I'll use int *ptr here, since int *a, b; declaring a as a pointer and b as an int is a classic gotcha the name-attached style makes clearer.)
Now assign an address to it:
Read &x as "the address of x". So ptr contains an address, and that address is x's location.
(Suggested image: two boxes — "x at 0x1004 holds 42" and "ptr at 0x1008 holds 0x1004" — with an arrow from ptr to x. Label the arrow "points to".)
Step 3: Dereferencing — Going to the Address
Holding an address is only useful if you can go there. That's dereferencing, written with a * in front of the pointer:
Sample output:
Two things to notice:
*ptrreads the value at the address — 42, the same valuexholds.*ptr = 99writes through the pointer — and since that address is x's address,xchanges to 99. You modifiedxwithout naming it.
The * is context-dependent, and this is the source of much early confusion:
One symbol, three roles. The compiler figures it out from context; you'll learn to read it too, quickly.
(Suggested image: the read and write paths — *ptr reading along the arrow to x, and *ptr = 99 writing back along the same arrow)
The Address-of and Dereference Relationship
These two operators are inverses. This identity always holds:
And in pointer terms:
If you can write those two lines from memory and explain them, you understand the core of pointers. That's genuinely the whole foundation.
(Suggested image: a circular diagram — x → (&) → ptr → () → x — showing the two operators as inverse arrows)*
Pointer Types: Why "Pointer to int" Isn't Just "Pointer"
Every pointer holds an address, and all addresses on a given system are the same size. So why does the type matter?
The type tells C two things:
1. How many bytes to read/write when dereferencing.*ip reads 4 bytes (an int); *cp reads 1 byte (a char). The address is the same kind of number, but dereferencing does different work.
2. How far to move in pointer arithmetic.ip + 1 moves 4 bytes forward; cp + 1 moves 1 byte. This is why arr + 1 points at the next element rather than the next byte — the type scales the arithmetic.
This is where arrays and pointers merge. nums[i] and *(nums + i) are defined to be identical in C — by the language standard, not as a trick. Array indexing is literally pointer arithmetic with friendlier syntax.
(Suggested image: p, p+1, p+2 above an int array's cells, with "4 bytes" marked between each — this diagram makes pointer arithmetic intuitive)
void * — The Typeless Pointer
A special case: void * holds any address, but you can't dereference it directly (the compiler doesn't know the size):
This is why qsort (from our function pointers tutorial) takes const void * and why you cast inside the comparison function — it's designed for any type, so it can't assume one.
The Size of Pointers
All pointers are the same size on a given platform — they're all just addresses. int * and char * differ in what they point to, not in their own storage size. (On 32-bit systems these would be 4 bytes.) This matters for the sizeof confusion in the arrays cluster: passing an array to a function gives you an 8-byte pointer, never the array's real size.
NULL: The Pointer That Points Nowhere
A pointer with no valid target should be set to NULL:
Why this matters so much: dereferencing NULL is a guaranteed crash (segmentation fault). Dereferencing an uninitialized pointer is worse — it might appear to work, silently corrupting memory, and fail somewhere unrelated later.
The habits:
- Always initialize pointers — to a real address or
NULL, never leave them dangling - Check for
NULLbefore dereferencing when a pointer comes from elsewhere (function returns,malloc, searching) - After freeing memory, set the pointer to
NULL(a "stale" pointer is as dangerous as a garbage one)
(Suggested image: three pointer states side by side — valid (arrow to a box), NULL (a crossed-out slash), and dangling/uninitialized (arrow pointing into scribbled memory) — with the last two marked dangerous)
Pointers and Arrays: The Connection
You've already seen hints of this throughout the series. Let's state it plainly:
This equivalence explains several things that came up in the Arrays cluster:
- Why arrays pass cheaply to functions — only the address travels (the "decay" we covered)
- Why
sizeoffails inside functions — the parameter is a pointer, sosizeofreports 8, not the array size - Why functions can modify your arrays — they hold the address and write through it
- Why
arr[i]and*(arr+i)are interchangeable — they're the same operation
Pointers and arrays aren't two topics that happen to overlap; in C, array indexing is pointer arithmetic wearing a friendly mask.
(Suggested image: the array with a pointer above element 0 and the equivalence nums[i] ≡ *(nums + i) highlighted)
What Pointers Are Actually For
Five real, practical uses — every one of which gets its own tutorial in this cluster.
1. Modifying a caller's variable (output parameters)
C is pass-by-value, so functions get copies. Pointers break that barrier:
This is the canonical first pointer program. Without pointers, swap couldn't work at all — it would swap its own copies and change nothing.
2. Returning multiple values
A function returns one thing — unless it writes through pointers:
This is the "output parameter" idiom: the caller supplies storage, the function fills it. It's how huge amounts of C library code returns extra results.
3. Dynamic memory allocation
Arrays are fixed-size at declaration. malloc gives you memory sized at runtime:
This is the only way to build data structures that grow — linked lists, trees, dynamic buffers. Its own tutorial coming up.
4. Efficiency with large data
Passing a big struct by value copies the whole thing; passing a pointer copies 8 bytes:
The const keeps it read-only (the habit from the array-parameters tutorial). Real C code uses pointer parameters for anything non-trivial, for exactly this reason.
5. Building data structures
Every linked list, tree, graph, and hash table node contains pointers to other nodes. Pointers are the structure. This is the destination of the whole cluster.
(Suggested image: five small icons representing the five uses — a swap arrow, a multi-output function, a growing heap block, a large struct, and a linked-list chain)
A Complete Example: A Pointer-Based Mini Toolkit
Sample Run:
Three pointer techniques in one program:
minMaxreturns two values viaint *minOut, int *maxOut— the only way in C's pass-by-value world.sumArraywalks with a pointer (p++moving 4 bytes per step, due to the type) and stops atarr + n, one past the end. Thearr + n"one past the end" pointer is legal to compute and compare — you just must never dereference it. That's a genuinely important detail.swap(&data[0], &data[n - 1])rearranges the actual array elements, because&data[0]and&data[n-1]are real addresses into your data.
And note the const on the read-only parameters — sumArray and minMax promise not to modify, which the compiler enforces. That's careful pointer code.
Common Pointer Mistakes
- Using an uninitialized pointer —
int *p; *p = 5;writes to a random address. Initialize to a real target orNULL. - Dereferencing
NULL— immediate segfault. Check before dereferencing whenever the pointer came from somewhere else. - The dangling pointer — a pointer to memory that's been freed or gone out of scope:
- Confusing the pointer with what it points to —
pis an address;*pis the value. Mixing them up produces garbage output and strange crashes. - Forgetting
&when passing to a function expecting a pointer —swap(x, y)instead ofswap(&x, &y). The compiler usually catches this (int vs int*), but not always. - Pointer type mismatches — assigning an
int *to achar *without a cast. Warnings exist for a reason; they signal real size errors at dereference time. - Losing the original pointer — if you
p++through amalloc'd array, you can'tfreeit anymore: - Confusing
&in scanf — you've been using pointers all along!scanf("%d", &x)passes x's address so scanf can write to it. That's the same mechanism asswap. - Using
==to compare strings through pointers — compares addresses, not contents. (From the string functions reference.)
(Suggested image: "9 Pointer Mistakes" checklist graphic — consistent with the series; the dangling pointer and uninitialized pointer cases as memory diagrams)
Final Thoughts
Pointers aren't a new kind of magic — they're a natural consequence of treating memory as a numbered place. A variable lives somewhere; its address is a number; you can store numbers in variables; therefore you can store addresses. That's the entire idea.
Everything else is detail: the type decides how many bytes a dereference touches and how far +1 moves; NULL marks a pointer with no target; * and & are inverse operators; and arrays are secretly pointers wearing nice syntax.
If this page felt heavy, that's expected — but notice that you've already been using pointers everywhere in this series: scanf("%d", &x) takes an address; array parameters are pointers; fgets fills through a pointer. You've had the tools all along; now you have the model.
Next in this cluster: pointer arithmetic in depth, where p++, pointer subtraction, and the array equivalence get their full treatment.
Runnable C Code Example & Practical Implementation
#include <stdio.h>
int main() {
int target = 42;
int *ptr = ⌖ // ptr stores address of target
printf("Value of target: %d\
", target);
printf("Address of target: %p\
",(void*)&target);
printf("Value stored in ptr:%p\
",(void*)ptr);
printf("Dereferenced *ptr: %d\
", *ptr);
*ptr = 99;
// Modifies target indirectly
printf("New target value: %d\
", target);
return 0;
}Key Takeaways, Best Practices & Memory Safety Rules
NULL (int *ptr = NULL;) immediately upon declaration.Frequently Asked Questions & Technical Interview Insights
Technical Interview & Viva Questions
Frequently asked C programming interview questionsQ: What is the size of `char*`, `int*`, and `double*` on a 64-bit architecture?
Q: What does dereferencing an uninitialized pointer cause?
Related Tools, Tutorials & Authoritative References
Recommended Interactive Tools
- Online C Compiler & Runner — Interactive online GCC sandbox to execute C code in real time.
- Online C Code Editor & IDE — Multi-file C programming environment with code formatting.
- C++ Programming Tutorial — Transition from procedural C to Object-Oriented C++ with STL.
- JavaScript Programming Tutorial — Master modern JavaScript fundamentals, DOM APIs, and async patterns.
Authoritative Standards & Citations
- ISO/IEC 9899 (C Language Standard) — Official international C specification by ISO/IEC JTC 1/SC 22/WG 14.
- GNU GCC Documentation — Official reference manual for the GNU Compiler Collection and C libraries.
- The C Programming Language (K&R) — Definitive classic work by Brian Kernighan & Dennis Ritchie at Bell Labs.
- C Reference Documentation (cppreference.com) — Verified reference covering C89 through C23 language features.