Docs
Pointers
Introduction to Pointers in C
Table of Contents
- •What are Pointers?
- •Memory and Addresses
- •Declaring Pointers
- •The Address-of Operator (&)
- •The Dereference Operator (*)
- •Pointer Initialization
- •NULL Pointers
- •Pointer Arithmetic Basics
- •Common Mistakes
- •Why Use Pointers?
- •Summary
What are Pointers?
A pointer is a variable that stores the memory address of another variable.
Regular Variable: Pointer Variable:
┌─────────────────┐ ┌─────────────────┐
│ value: 42 │ │ value: 1000 │ ← Address!
│ address: 1000 │ │ address: 2000 │
└─────────────────┘ └─────────────────┘
x ptr
│
└── Points to address 1000 (where x is)
Simple Analogy
Think of memory addresses like house addresses:
- •A variable is like a house that holds a value (the people living there)
- •A pointer is like a piece of paper with an address written on it
- •The pointer doesn't contain the value itself - just where to find it
Memory and Addresses
How Memory Works
Computer memory is organized as a sequence of bytes, each with a unique address.
Memory Layout:
Address: 1000 1001 1002 1003 1004 1005 1006 1007
┌──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┐
│ │ │ │ │ │ │ │ │
└──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┘
int x = 42; (int takes 4 bytes)
Address: 1000 1001 1002 1003 1004 1005 1006 1007
┌──────────────────────────┬──────────────────────────┐
│ x = 42 (4 bytes) │ (other data) │
└──────────────────────────┴──────────────────────────┘
Size of Types in Memory
| Type | Typical Size | Example Address Range |
|---|---|---|
| char | 1 byte | 1000 |
| short | 2 bytes | 1000-1001 |
| int | 4 bytes | 1000-1003 |
| long | 8 bytes | 1000-1007 |
| float | 4 bytes | 1000-1003 |
| double | 8 bytes | 1000-1007 |
| pointer | 8 bytes (64-bit) | 1000-1007 |
Declaring Pointers
Syntax
type *pointer_name;
The * indicates this is a pointer to the specified type.
Examples
int *ptr; // Pointer to int
char *cptr; // Pointer to char
float *fptr; // Pointer to float
double *dptr; // Pointer to double
Style Variations
All of these declare an int pointer:
int *ptr; // * with variable name (preferred)
int* ptr; // * with type
int * ptr; // * with spaces
Warning: Multiple Declarations
int *p1, *p2; // TWO pointers
int *p1, p2; // p1 is pointer, p2 is int (NOT pointer!)
int *p1, *p2; // Always use * with each name
The Address-of Operator (&)
The & operator returns the memory address of a variable.
int x = 42;
printf("Value of x: %d\n", x); // 42
printf("Address of x: %p\n", &x); // 0x7ffd5e8c3abc (example)
Visual Representation
int x = 42;
int *ptr = &x;
Memory:
┌─────────────────────────────────────────┐
│ Address 1000-1003: x = 42 │
├─────────────────────────────────────────┤
│ Address 2000-2007: ptr = 1000 │
└─────────────────────────────────────────┘
&x returns 1000
ptr stores 1000
Using with scanf
int num;
scanf("%d", &num); // Pass address of num
// scanf needs to know WHERE to store the input
The Dereference Operator (*)
The * operator (when used with a pointer) accesses the value at the address the pointer holds.
int x = 42;
int *ptr = &x;
printf("Address stored in ptr: %p\n", ptr); // Address of x
printf("Value at that address: %d\n", *ptr); // 42 (value of x)
Reading and Writing Through Pointers
int x = 42;
int *ptr = &x;
// Reading
int value = *ptr; // value = 42
// Writing
*ptr = 100; // x is now 100!
printf("x = %d\n", x); // Output: 100
Visual Example
int x = 42;
int *ptr = &x;
*ptr = 100;
Step 1: Declaration
┌───────────┐ ┌───────────┐
│ x = 42 │ │ ptr = &x │
│ addr:1000 │ ←── │ (1000) │
└───────────┘ └───────────┘
Step 2: *ptr = 100 (write through pointer)
┌───────────┐ ┌───────────┐
│ x = 100 │ ←── │ ptr = &x │ *ptr means "go to address
│ addr:1000 │ │ (1000) │ 1000 and change value"
└───────────┘ └───────────┘
Pointer Initialization
Always Initialize Pointers
// GOOD - initialized to valid address
int x = 10;
int *ptr = &x;
// GOOD - initialized to NULL
int *ptr = NULL;
// BAD - uninitialized (dangerous!)
int *ptr; // Contains garbage address
*ptr = 5; // CRASH! Accessing random memory
Different Ways to Initialize
int x = 42;
int *ptr1 = &x; // Initialize with address
int *ptr2 = NULL; // Initialize to null
int *ptr3 = ptr1; // Initialize with another pointer
NULL Pointers
NULL is a special value indicating "points to nothing."
#include <stdio.h>
#include <stdlib.h> // or <stddef.h> for NULL
int *ptr = NULL; // Safe - clearly indicates "no valid address"
Checking for NULL
int *ptr = NULL;
if (ptr == NULL) {
printf("Pointer is NULL\n");
}
// Or equivalently:
if (!ptr) {
printf("Pointer is NULL\n");
}
Why Use NULL?
1. Indicate uninitialized state
2. Indicate end of data structure
3. Indicate function failure (return NULL on error)
4. Safe default value (crashes are easier to debug than corruption)
Pointer Arithmetic Basics
When you add/subtract from a pointer, it moves by the size of the pointed-to type.
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr; // Points to arr[0]
printf("%d\n", *ptr); // 10 (arr[0])
printf("%d\n", *(ptr + 1)); // 20 (arr[1])
printf("%d\n", *(ptr + 2)); // 30 (arr[2])
Visual: Pointer Arithmetic
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr;
Memory (int = 4 bytes):
Address: 1000 1004 1008 1012 1016
┌─────────┬─────────┬─────────┬─────────┬─────────┐
│ 10 │ 20 │ 30 │ 40 │ 50 │
└─────────┴─────────┴─────────┴─────────┴─────────┘
↑ ↑ ↑
ptr ptr + 1 ptr + 2
(1000) (1004) (1008)
ptr + 1 doesn't add 1 byte, it adds sizeof(int) = 4 bytes!
Type Matters
char *cp = (char *)1000; // Assume address 1000
int *ip = (int *)1000;
cp + 1 → 1001 (moves 1 byte)
ip + 1 → 1004 (moves 4 bytes)
Common Mistakes
1. Using Uninitialized Pointers
// WRONG
int *ptr;
*ptr = 42; // CRASH! ptr points to random location
// RIGHT
int x;
int *ptr = &x;
*ptr = 42;
2. Confusing & and *
int x = 42;
int *ptr = &x;
&x → address of x (a number like 0x1000)
*ptr → value at address ptr points to (42)
ptr → the address stored in ptr
// These are equivalent:
x == *ptr // Both give 42
&x == ptr // Both give address of x
3. Wrong Type of Pointer
int x = 42;
float *ptr = &x; // WARNING! Type mismatch
*ptr = 3.14; // Writes float bit pattern into int memory
// Result is garbage
4. Dereferencing NULL
int *ptr = NULL;
*ptr = 42; // CRASH! Segmentation fault
5. Returning Address of Local Variable
int* badFunction(void) {
int local = 42;
return &local; // WRONG! local doesn't exist after function returns
}
Why Use Pointers?
1. Modify Variables in Functions
void increment(int *ptr) {
(*ptr)++; // Modifies original variable
}
int main(void) {
int x = 5;
increment(&x);
printf("%d\n", x); // Output: 6
}
2. Efficient Data Passing
// Without pointer: copies entire struct (slow)
void processData(struct BigStruct data);
// With pointer: passes just an address (fast)
void processData(struct BigStruct *data);
3. Dynamic Memory Allocation
int *arr = malloc(100 * sizeof(int)); // Allocate array at runtime
// ... use arr ...
free(arr);
4. Work with Arrays and Strings
char *str = "Hello"; // Pointer to string
int arr[5];
int *ptr = arr; // Pointer to array
5. Build Data Structures
struct Node {
int data;
struct Node *next; // Pointer to next node
};
Summary
Key Concepts
| Concept | Symbol | Meaning |
|---|---|---|
| Pointer declaration | int *ptr | Declares a pointer to int |
| Address-of | &x | Gets address of x |
| Dereference | *ptr | Gets value at address ptr points to |
| NULL pointer | NULL | Pointer that points to nothing |
Pointer Operations
int x = 42;
int *ptr = &x; // ptr holds address of x
*ptr // Read value at address (42)
*ptr = 100 // Write value at address (x becomes 100)
ptr + 1 // Address of next int in memory
ptr == NULL // Check if pointer is null
Memory Picture
Variable x at address 1000:
┌──────────┐
│ x = 42 │
│ @1000 │
└──────────┘
Pointer ptr at address 2000:
┌──────────┐
│ptr= 1000 │ ───→ points to x
│ @2000 │
└──────────┘
&x = 1000 (address of x)
ptr = 1000 (value stored in ptr)
*ptr = 42 (value at address 1000)
&ptr = 2000 (address of ptr itself)
Golden Rules
✓ Always initialize pointers (to valid address or NULL)
✓ Check for NULL before dereferencing
✓ Use correct pointer type for the data
✓ Don't return addresses of local variables
✓ Free dynamically allocated memory
Next Steps
After understanding pointer basics:
- •Learn Pointers and Arrays relationship
- •Study Pointer to Pointer (double pointers)
- •Explore Dynamic Memory Allocation
- •Understand Function Pointers
"A pointer is just a variable that holds an address - nothing more, nothing less."