Pointers in C
The most powerful feature in C! Pointers store memory addresses. Learn what they are, why they matter, and how to use them safely.
Track Your Progress
Sign in to save your learning progress
What You Will Learn
- ✓Understand what pointers are (address holders)
- ✓Use & to get address, * to get value
- ✓Declare and initialize pointers
- ✓Understand NULL pointers and why they matter
01What is a Pointer?
A pointer is a variable that stores the memory address of another variable. Instead of holding data directly, it "points to" where the data is stored in memory.
Variable vs Pointer
Regular Variable
Stores the actual value
Pointer Variable
Stores the address of another variable
Why Use Pointers?
- • Efficiency: Pass large data without copying
- • Dynamic Memory: Allocate memory at runtime
- • Data Structures: Build linked lists, trees, graphs
- • Function Parameters: Modify variables from functions
Real-World Analogy: House Addresses
Think of memory like a street with houses:
Regular Variable = House Contents
Like the furniture inside a house. You store actual things (data) in it.
Pointer = House Address
Like a piece of paper with an address written on it. It doesn't contain furniture, but tells you where to find it!
Just like you can give someone your address (pointer) instead of moving your entire house (copying data), pointers let you share location instead of duplicating data!
02Understanding Memory Addresses
Every variable in your program is stored at a specific location in memory. This location has a unique address (like a house number on a street).
1#include <stdio.h>23int main() {4 int age = 25;5 float price = 99.99;6 char grade = 'A';7 8 // Print the VALUE of variables9 printf("age = %d\n", age);10 printf("price = %.2f\n", price);11 printf("grade = %c\n", grade);12 13 // Print the ADDRESS of variables using & operator14 printf("\nAddress of age: %p\n", (void*)&age);15 printf("Address of price: %p\n", (void*)&price);16 printf("Address of grade: %p\n", (void*)&grade);17 18 return 0;19}Sample Output:
price = 99.99
grade = A
Address of age: 0x7ffd5e8e3a4c
Address of price: 0x7ffd5e8e3a48
Address of grade: 0x7ffd5e8e3a47
The & Operator (Address-of)
&variable returns the memory address of a variable. The address is shown in hexadecimal format (0x...).
What's Really Happening in Memory?
When you create a variable like int age = 25;, here's what happens:
Computer reserves space — The computer sets aside 4 bytes of RAM (since int is 4 bytes) at some location, say 0x1000.
Value is stored — The number 25 is converted to binary and stored in those 4 bytes.
Name becomes shortcut — "age" becomes a convenient label so you don't need to remember 0x1000.
Think of it like a hotel: the room number (address) never changes, but who's staying in it (value) can change!
03Declaring and Initializing Pointers
1#include <stdio.h>23int main() {4 // Step 1: Create a regular variable5 int num = 42;6 7 // Step 2: Declare a pointer (use * in declaration)8 int *ptr; // ptr can store address of an int9 10 // Step 3: Assign address to pointer (use & to get address)11 ptr = # // ptr now "points to" num12 13 // Combined declaration and initialization14 int value = 100;15 int *p = &value; // Declare and initialize in one line16 17 // Print to verify18 printf("num = %d\n", num); // Value: 4219 printf("&num = %p\n", (void*)&num); // Address of num20 printf("ptr = %p\n", (void*)ptr); // Same address (ptr stores it)21 printf("*ptr = %d\n", *ptr); // Value at that address: 4222 23 return 0;24}How This Program Works
int num = 42 — Creates a variable storing 42 at some memory address (e.g., 0x1000).
int *ptr — Declares a pointer variable. The * means "pointer to int". Currently contains garbage.
ptr = &num — The & gets num's address (0x1000). Now ptr stores that address.
printf("%p", ptr) — Prints what ptr contains: the address 0x1000 (same as &num).
*ptr — The * dereferences: "go to address in ptr and get the value there" → returns 42.
Pointer Syntax Summary
| Syntax | Meaning | Example |
|---|---|---|
| int *ptr; | Declare pointer to int | Creates pointer variable |
| &num | Get address of num | Returns 0x7ffd... |
| ptr = # | Store address in ptr | ptr points to num |
| *ptr | Get value at address (dereference) | Returns 42 |
04Dereferencing Pointers (*)
Dereferencing means accessing the value stored at the address the pointer holds. Use the * operator.
1#include <stdio.h>23int main() {4 int x = 10;5 int *ptr = &x; // ptr points to x6 7 // Reading via pointer8 printf("Value of x: %d\n", x); // Direct: 109 printf("Value via *ptr: %d\n", *ptr); // Via pointer: 1010 11 // Modifying via pointer12 *ptr = 50; // Changes the value at address (changes x!)13 14 printf("\nAfter *ptr = 50:\n");15 printf("Value of x: %d\n", x); // Now 50!16 printf("Value via *ptr: %d\n", *ptr); // Also 5017 18 // Both x and *ptr refer to the same memory location19 x = 100;20 printf("\nAfter x = 100:\n");21 printf("Value via *ptr: %d\n", *ptr); // 10022 23 return 0;24}Output:
Value via *ptr: 10
After *ptr = 50:
Value of x: 50
Value via *ptr: 50
After x = 100:
Value via *ptr: 100
How Dereferencing Works
int *ptr = &x — ptr now holds x's memory address. Both x and *ptr refer to the same location.
*ptr (reading) — "Go to the address stored in ptr and read what's there" → returns 10.
*ptr = 50 (writing) — "Go to the address stored in ptr and write 50 there". This changes x!
After *ptr = 50, printing x shows 50 — proof that x and *ptr are the same memory.
Changing x = 100 also makes *ptr return 100. They're always in sync!
The * Has Two Meanings!
- • In declaration:
int *ptr;— defines a pointer - • In expression:
*ptr— dereferences (gets value)
05Pointer Memory Layout
A pointer is itself a variable that takes up memory. On most modern systems, pointers are 8 bytes (64-bit) or 4 bytes (32-bit).
int x = 42; int *ptr = &x;
Variable x
@0x1000
4 bytes
Pointer ptr
@0x2000
8 bytes (64-bit)
ptr stores the address 0x1000, which is where x lives in memory
1#include <stdio.h>23int main() {4 int x = 42;5 int *ptr = &x;6 7 printf("Size of int: %zu bytes\n", sizeof(int)); // 48 printf("Size of int*: %zu bytes\n", sizeof(int*)); // 8 (on 64-bit)9 printf("Size of ptr: %zu bytes\n", sizeof(ptr)); // 810 11 // All pointer types are the same size!12 char *cp;13 double *dp;14 printf("\nSize of char*: %zu bytes\n", sizeof(cp)); // 815 printf("Size of double*: %zu bytes\n", sizeof(dp)); // 816 17 return 0;18}06Pointer Arithmetic
You can add or subtract integers from pointers. The pointer moves by the size of the data type it points to.
1#include <stdio.h>23int main() {4 int arr[5] = {10, 20, 30, 40, 50};5 int *ptr = arr; // Points to first element6 7 printf("ptr points to: %d (address: %p)\n", *ptr, (void*)ptr);8 9 // ptr + 1 moves by sizeof(int) = 4 bytes10 ptr++; // or ptr = ptr + 111 printf("After ptr++: %d (address: %p)\n", *ptr, (void*)ptr);12 13 ptr++;14 printf("After ptr++: %d (address: %p)\n", *ptr, (void*)ptr);15 16 // Can also use pointer arithmetic directly17 printf("\nUsing arr pointer arithmetic:\n");18 printf("*(arr + 0) = %d\n", *(arr + 0)); // 1019 printf("*(arr + 1) = %d\n", *(arr + 1)); // 2020 printf("*(arr + 2) = %d\n", *(arr + 2)); // 3021 22 // arr[i] is equivalent to *(arr + i)23 printf("\narr[3] = %d, *(arr + 3) = %d\n", arr[3], *(arr + 3));24 25 return 0;26}Pointer Arithmetic Visualization
+0
+4
+8
+12
+16
ptr → ptr+1 → ptr+2 → ptr+3 → ptr+4
Each step moves by sizeof(int) = 4 bytes
07Pointers and Arrays
Arrays and pointers are closely related. The array name is actually a pointer to its first element!
1#include <stdio.h>23int main() {4 int arr[5] = {10, 20, 30, 40, 50};5 6 // Array name IS a pointer to first element7 printf("arr = %p\n", (void*)arr);8 printf("&arr[0] = %p\n", (void*)&arr[0]); // Same address!9 10 // These are equivalent ways to access elements:11 printf("\nAccessing arr[2]:\n");12 printf("arr[2] = %d\n", arr[2]); // Subscript notation13 printf("*(arr + 2) = %d\n", *(arr + 2)); // Pointer arithmetic14 15 // Using a separate pointer16 int *ptr = arr; // No & needed - arr is already an address17 18 printf("\nUsing pointer:\n");19 for (int i = 0; i < 5; i++) {20 printf("ptr[%d] = %d, *(ptr + %d) = %d\n", 21 i, ptr[i], i, *(ptr + i));22 }23 24 return 0;25}Key Equivalences
- •
arr[i]≡*(arr + i) - •
&arr[i]≡arr + i - •
arr≡&arr[0]
08NULL Pointer
A NULL pointer is a pointer that doesn't point to any valid memory location. It's used to indicate "no value" or "not pointing to anything".
1#include <stdio.h>2#include <stdlib.h> // For NULL34int main() {5 int *ptr = NULL; // Initialize to NULL6 7 // Always check before dereferencing!8 if (ptr == NULL) {9 printf("ptr is NULL - cannot dereference!\n");10 } else {11 printf("Value: %d\n", *ptr);12 }13 14 // Now point to something valid15 int x = 42;16 ptr = &x;17 18 if (ptr != NULL) {19 printf("ptr is valid, value: %d\n", *ptr);20 }21 22 // Common pattern: set to NULL after freeing23 // free(ptr); // If dynamically allocated24 ptr = NULL; // Prevent dangling pointer25 26 return 0;27}Never Dereference NULL!
Dereferencing a NULL pointer causes a segmentation fault (crash). Always check if a pointer is NULL before using *ptr.
!Code Pitfalls: Common Mistakes & What to Watch For
These are the most common mistakes that trip up beginners. Study them carefully to avoid hours of debugging!
Uninitialized Pointer (Wild Pointer)
int *ptr; // Points to random memory!*ptr = 10; // CRASH - undefined behavior// Always initialize:int *ptr = NULL; // SafeDangling Pointer
int *ptr;{ int x = 10; ptr = &x;} // x is destroyed here!printf("%d", *ptr); // UNDEFINED - x no longer existsMismatched Pointer Types
float f = 3.14;int *ptr = &f; // WARNING - type mismatch!// Correct:float *ptr = &f; // Types must matchForgetting & in scanf
int x;scanf("%d", x); // WRONG! Missing &scanf("%d", &x); // Correct - pass addressWatch Out When Copying Code!
Pointers: Where Beginners Fail Most!
Pointers are C's most powerful feature — and where beginners make its most dangerous mistakes. These bugs often compile without warnings but crash at runtime or corrupt memory:
1. Dereferencing Uninitialized Pointers
Copied code might declare a pointer and use it without assigning an address:
int *ptr; // Points to random memory!*ptr = 42; // CRASH or memory corruption// Correct:int x;int *ptr = &x; // Points to valid memory*ptr = 42;2. Returning Pointer to Local Variable
Copied code often generates code that returns a pointer to a local variable — which is destroyed when the function ends:
int* getNumber() { int x = 42; return &x; // BUG! x dies here}// Returned pointer is INVALID!3. Confusing * in Declarations vs Expressions
Beginners sometimes gets confused between int *p = &x (declare pointer) and *p = 5 (dereference existing pointer). Mixing these up causes subtle bugs.
Safety Rule: Always initialize pointers. Either to a valid address (&variable) or to NULL. Then check for NULL before dereferencing.
12Frequently Asked Questions
Q:What's the difference between * in declarations vs expressions?
A: In a declaration,int *p means "p is a pointer to int." In an expression, *p means "get the value that p points to" (dereference). Same symbol, different meanings based on context. Think: declaration = defining type, expression = accessing value.
Q:Why do pointers have types (int*, char*, etc.)?
A: The pointer type tells the compiler two things: (1) How many bytes to read/write when dereferencing (int* reads 4 bytes,char* reads 1 byte), and (2) How much to increment in pointer arithmetic (p+1 moves 4 bytes for int*, 1 byte for char*). All pointers store addresses (8 bytes on 64-bit), but they interpret memory differently.
Q:What is NULL and when should I use it?
A: NULL is a special value (usually 0) representing "points to nothing." Use it to: (1) Initialize pointers that don't yet have a target, (2) Indicate "no result" from functions, (3) Check if a pointer is valid before dereferencing. Always if (p != NULL) before*p. Dereferencing NULL causes a crash (segmentation fault).
Q:What's the relationship between pointers and arrays?
A: Array names "decay" to pointers in most contexts.arr becomes &arr[0]. You can use pointer arithmetic: *(arr + i) equalsarr[i]. Key difference: arrays have fixed memory, pointers can be reassigned. sizeof(arr) gives array size;sizeof(ptr) gives pointer size (8 bytes).
Q:What is a double pointer (int **)?
A: A pointer to a pointer.int **pp stores the address of anint*. Common uses: (1) Modifying a pointer inside a function, (2) 2D arrays via dynamic allocation, (3) Arrays of strings (char**like argv). To get the final int: **pp (dereference twice).
Q:What is a void pointer (void*)?
A: A "generic" pointer that can hold any address but has no type information. void* can't be dereferenced directly — you must cast it first: *(int*)vp. Used for generic functions like malloc() (returns void*) andqsort() comparators. Powerful but requires careful type handling.
Q:Why is my pointer printing a weird large number?
A: You're probably printing the address, not the value!printf("%d", ptr) prints the address (large hex number).printf("%d", *ptr) prints the value it points to. Use %p format for addresses:printf("%p", (void*)ptr).
12Summary
What You Learned:
- ✓WHY: Pointers let us pass data efficiently and modify variables in functions
- ✓& operator: Gets the address of a variable
- ✓* operator: Gets the value at an address (dereference)
- ✓NULL: Always initialize pointers to NULL if no value
Test Your Knowledge
Related Tutorials
Linked Lists in C
Learn the fundamental data structure of linked lists. Create singly linked lists, doubly linked lists, and perform operations like insertion, deletion, and traversal.
C stddef.h Library Reference
Complete reference for stddef.h - NULL, size_t, ptrdiff_t, offsetof, and fundamental type definitions.
Runtime Errors and Exceptions in C
Understand runtime errors in C: Division by zero, Segmentation faults, Memory leaks, Buffer overflows. Learn prevention and error handling techniques.
Have Feedback?
Found something missing or have ideas to improve this tutorial? Let us know on GitHub!