Pointers are variables that hold memory addresses as their value. They are the mechanism that makes dynamic allocation, linked data structures, and modifying a function's parameters possible. They are also the source of the most frequent serious bugs in a C program.
1Lab objectives
- Declaring pointers and understanding the role of the
*symbol - Using the
&(address) and*(dereference) operators - Applying the rules of pointer arithmetic
- Exploiting the link between pointers and arrays
- Modifying a function's parameters through pointers and through references
2What a pointer is
Every variable occupies a memory area that has an address. A pointer is an ordinary variable, with the difference that the value it holds is precisely such an address.
| Category | Contents | Example |
|---|---|---|
| Data pointers | addresses of variables or constants | int *p = &x; |
| Function pointers | addresses of executable code | int (*f)(int); |
3Declaration and operators
type * variable_name; int *p_i; // pointer to int float *p_f; // pointer to float int *tab_p[10]; // array of 10 pointers to integers float **p_p; // pointer to pointer void *p_v; // generic pointer: can hold the address of any type
| Operator | Name | What it does | Example |
|---|---|---|---|
& | reference-of | returns the address of a variable | p = &x; |
* (in a declaration) | type marker | shows that the variable is a pointer | int *p; |
* (in expressions) | dereference | accesses the value at the stored address | y = *p; |
int *p; the star is part
of the type. In *p = 7; the star is an operator meaning "write to the address held
by p". The distinction is made by context: declaration or expression.void * can hold the address of any data
type, but cannot be dereferenced directly - the compiler does not know how many bytes to
read. An explicit conversion is required: *(int *)p_v.4Memory simulator
Change the pointer's target and watch the difference between
p, *p, and &p. Select NULL to see what a pointer
that points to nothing means.
5Pointer arithmetic
Adding an integer to a pointer does not add bytes, it adds elements. The compiler automatically multiplies by the size of the pointed-to type.
| Operation | Actual effect | For int * (4 bytes) |
|---|---|---|
p + 1 | advances by 1 element | the address increases by 4 |
p + n | advances by n elements | the address increases by 4·n |
p - q | the number of elements between them | (the address difference) / 4 |
p++ | moves to the next element | the address increases by 4 |
p + 1 advances by 1 byte
for char *, by 4 for int *, and by 8 for double *. The
pointer's type is essential, not decorative.6Pointers and arrays
An array's name is, in most contexts, precisely the address of the first element. That is why indexing and pointer arithmetic are equivalent notations:
| Index notation | Pointer notation | Meaning |
|---|---|---|
v | &v[0] | the address of the first element |
v[i] | *(v + i) | the element at position i |
&v[i] | v + i | the address of element i |
p[i] | *(p + i) | works for pointers too |
sizeof(v) for int v[10] gives 40, while sizeof(p) for
int *p gives 4 or 8 - the size of an address. Also, the array's name cannot be
modified: v++ is an error, p++ is allowed.7Passing parameters to functions
In C, parameters are passed by value: the function receives a copy. Changes made inside are lost on return. To modify the original variable, its address must be passed.
| Mode | Syntax | Can modify the original? | Available in |
|---|---|---|---|
| By value | void f(int x) | no | C and C++ |
| By pointer | void f(int *x) | yes, with *x = ... | C and C++ |
| By reference | void f(int &x) | yes, directly with x = ... | C++ only |
8Reference variables (C++ only)
A reference is an alternative name for an existing variable. Unlike a pointer, it occupies no memory of its own and has no special syntax when used.
int x = 10; int &r = x; // r becomes another name for x r = 20; // modifies x directly, with no star cout << x; // prints 20
| Aspect | Pointer | Reference |
|---|---|---|
| Must be initialized at declaration | no | yes, mandatory |
| Can be redirected to something else | yes | no, never |
| Can be NULL | yes | no |
| Syntax when used | *p | r (like an ordinary variable) |
| Occupies its own memory | yes | usually not |
9Common mistakes
int *p; *p = 5; writes to a random address - the program crashes or, worse,
corrupts other data. Always initialize: int *p = NULL;if (p != NULL) { *p = 5; }int* wrong() { int x = 5; return &x; }int *p, q; declares one pointer and one ordinary integer, not two
pointers. The star applies to each variable separately: int *p, *q;10Source code
#include <stdio.h>
int main(void)
{
int x = 42;
int *p = &x; // p receives the address of x
printf("x = %d\n", x); // 42
printf("&x = %p\n", (void*)&x);
printf("p = %p\n", (void*)p); // the same address
printf("*p = %d\n", *p); // 42 - the value at that address
printf("&p = %p\n", (void*)&p); // where the pointer itself is stored
*p = 100; // modifies x through the pointer
printf("\nafter *p = 100, x = %d\n", x); // 100
printf("sizeof(x) = %zu, sizeof(p) = %zu\n", sizeof(x), sizeof(p));
return 0;
}
#include <stdio.h>
int main(void)
{
int v[5] = {10, 20, 30, 40, 50};
int *p = v; // equivalent to &v[0]
printf("By index: ");
for (int i = 0; i < 5; i++) printf("%d ", v[i]);
printf("\nBy pointer: ");
for (int i = 0; i < 5; i++) printf("%d ", *(p + i));
printf("\nWith increment: ");
p = v;
for (int i = 0; i < 5; i++) printf("%d ", *p++);
// addresses increase by 4 each time
printf("\n\nAddresses:\n");
for (int i = 0; i < 5; i++)
printf(" &v[%d] = %p\n", i, (void*)&v[i]);
// the difference of two pointers = the number of elements between them
int *start = &v[0], *end = &v[4];
printf("\nElements between start and end: %ld\n", end - start);
return 0;
}
#include <stdio.h>
void swapWrong(int a, int b) // receives COPIES
{
int tmp = a; a = b; b = tmp; // swaps only the copies
}
void swapCorrect(int *a, int *b) // receives ADDRESSES
{
int tmp = *a; *a = *b; *b = tmp;
}
int main(void)
{
int x = 1, y = 2;
swapWrong(x, y);
printf("After swapWrong: x=%d y=%d\n", x, y); // 1 2 - unchanged
swapCorrect(&x, &y);
printf("After swapCorrect: x=%d y=%d\n", x, y); // 2 1 - swapped
return 0;
}
#include <iostream>
using namespace std;
void swapRef(int &a, int &b) // references: clean syntax
{
int tmp = a; a = b; b = tmp; // no star
}
void addTo(int &value, int amount)
{
value += amount;
}
int main()
{
int x = 1, y = 2;
swapRef(x, y);
cout << "x=" << x << " y=" << y << endl; // 2 1
addTo(x, 10);
cout << "x after adding = " << x << endl; // 12
// a reference cannot be redirected
int a = 5, b = 9;
int &r = a;
r = b; // NOTE: copies the value of b into a,
// does NOT make r refer to b
cout << "a=" << a << " b=" << b << endl; // 9 9
return 0;
}
11Code workshop
Here Step by step mode makes the difference: the panel on the right shows the address of each variable and, for pointers, exactly what they point to at that moment.
p row in the
panel on the right: the address increases by 8 (that is, 2 x 4 bytes), and the label on the
right changes from v[0] to v[2].12Work tasks
- Declare a variable and a pointer to it; print the value, the address, and the result of dereferencing.
- Modify the variable exclusively through the pointer and check the effect.
- Compare
sizeoffor anint, adouble, and the pointers to them. - Traverse an array three ways: with an index, with
*(p+i), and with*p++. - Print the addresses of the elements and check that the difference is
sizeof(type). - Implement the swap function, first incorrectly (by value), then correctly (by pointer).
- Write a function that receives a vector and returns both the minimum and the maximum through pointers.
- In C++, rewrite the swap using references and compare the readability of the code.
13Extended application
int (*operations[4])(int, int)) that lets you select the arithmetic operation from
a menu.