What you'll learn
A pointer stores the address of an object or function. Pointers let C share storage across function boundaries, traverse arrays efficiently, express optional values, and manage dynamic resources.
By the end, you'll be able to:
- Create pointers with the address-of operator and access objects by dereferencing
- Use pointer parameters to modify caller-owned objects
- Traverse arrays with valid pointer arithmetic
- Apply null checks and const correctly
Objects & addresses
Every live object occupies storage. The unary & operator obtains its address. A pointer's type describes the kind of object expected at that address.
pointer variable
score_ptr
0x7ff...a4
int object
score
92
Key idea
NULL.Declare & dereference
In a declaration, * introduces a pointer type. In an expression,*pointer accesses the pointed-to object. This second use isdereferencing.
#include <stdio.h>
int main(void) {
int score = 92;
int *score_ptr = &score;
printf("value: %d\n", score);
printf("through pointer: %d\n", *score_ptr);
printf("address: %p\n", (void *) score_ptr);
*score_ptr = 100;
printf("updated: %d\n", score);
return 0;
}Pointer parameters
C still passes a pointer argument by value, but the copied address refers to the caller's object. Dereferencing it lets the function update that shared object.
#include <stdio.h>
void swap(int *left, int *right) {
int temporary = *left;
*left = *right;
*right = temporary;
}
int main(void) {
int first = 10;
int second = 20;
swap(&first, &second);
printf("%d %d\n", first, second);
return 0;
}Note
Null & invalid pointers
| State | Meaning | Safe action |
|---|---|---|
NULL | Deliberately points to no object | Compare; do not dereference |
| Uninitialized (wild) | Contains an indeterminate address | Initialize before any use |
| Dangling | Former object no longer exists | Stop using; reset when useful |
| Valid | Points to a live compatible object | Dereference within its permissions |
#include <stdio.h>
void print_value(const int *value) {
if (value == NULL) {
printf("No value\n");
return;
}
printf("%d\n", *value);
}
int main(void) {
int answer = 42;
print_value(&answer);
print_value(NULL);
return 0;
}Watch out
Pointer arithmetic
Adding one to an int * advances by one int, not one byte. Pointer arithmetic is defined only within one array object and its one-past position.
#include <stdio.h>
int main(void) {
int values[] = {10, 20, 30, 40};
int *cursor = values;
int *end = values + 4;
while (cursor != end) {
printf("%d ", *cursor);
cursor++;
}
putchar('\n');
return 0;
}Watch out
Pointers and arrays
In most expressions, an array name converts to a pointer to its first element. Indexing is defined in terms of pointer arithmetic: values[i] means*(values + i).
#include <stddef.h>
#include <stdio.h>
int sum(const int *values, size_t count) {
int total = 0;
for (size_t i = 0; i < count; i++) {
total += *(values + i); // same element as values[i]
}
return total;
}
int main(void) {
int values[] = {2, 4, 6, 8};
printf("%d\n", sum(values, 4));
return 0;
}Key idea
const correctness
void examples(void) {
int value = 10;
int other = 20;
const int *read_only = &value; // may point elsewhere; cannot change *read_only
int *const fixed = &value; // cannot point elsewhere; may change *fixed
const int *const both = &value; // neither operation is allowed
read_only = &other;
*fixed = 11;
(void) read_only;
(void) both;
}Read declarations from the identifier outward. const int *p is a pointer to const int; int *const p is a const pointer to int.
Pointers to pointers
A pointer can itself have an address. An int ** is useful when a function must update an int * owned by its caller. Dynamic allocation and linked structures will use this pattern.
#include <stdio.h>
void choose_larger(int **selection, int *left, int *right) {
*selection = *left >= *right ? left : right;
}
int main(void) {
int a = 7;
int b = 12;
int *chosen = NULL;
choose_larger(&chosen, &a, &b);
printf("%d\n", *chosen);
return 0;
}Recap & quick check
Key takeaways
- The address-of operator creates an address value; dereferencing accesses the pointed-to object.
- Pointer parameters let functions work with caller-owned storage while the pointer itself is passed by value.
- NULL represents no object and must not be dereferenced.
- Pointer arithmetic is constrained to a single array and its one-past position.
- Arrays often convert to pointers in expressions, but arrays and pointers remain distinct concepts.
Quick check
1. What does &score produce?
2. What does *ptr mean in an expression?
3. Where may pointer arithmetic be performed portably?
4. What does const int *value promise?
You can now reason with addresses. Next up: Module 10 — Dynamic Memory Management, where object sizes and lifetimes can be chosen at runtime.