The asterisk in C and C++ pointers
In C, * in a declaration means "pointer to": int *p; makes p a pointer to an int. * in an expression means "the thing it points to": *p = 5; stores 5 at that address. Between two values, * is still plain multiplication.
Declare, take the address, dereference
Three symbols work together. * in a declaration makes a pointer, & gets the address of a variable, and * in an expression follows the pointer:
#include <stdio.h>
int main(void) {
int x = 10;
int *p = &x; /* p holds the address of x */
*p = 42; /* write through the pointer */
printf("%d\n", x); /* 42 */
printf("%d\n", *p * 2); /* 84: dereference, then multiply */
return 0;
}
Following a pointer is called dereferencing. Doing it on a pointer that's NULL or points at memory you've freed is undefined behavior, and usually a crash.
int *p or int* p?
To the compiler, int *p, int* p and int * p are the same declaration. The spacing is a style choice, but one detail makes it matter:
int* a, b; /* a is a pointer; b is a plain int */
int *a, *b; /* both are pointers */
The * belongs to each name, not to the type. That's why most C style guides write int *a, keeping the star next to the name it applies to. Many C++ codebases prefer int* a and declare one variable per line, which sidesteps the problem.
Pointers to pointers
Each extra * adds a level. char **argv in main is a pointer to a pointer to char: a list of strings.
#include <stdio.h>
int main(int argc, char **argv) {
for (int i = 0; i < argc; i++)
printf("%d: %s\n", i, argv[i]);
return 0;
}
In a parameter list, char **argv and char *argv[] mean exactly the same thing.
Reading tricky declarations
Start at the name and work outward, reading right before left, and treat parentheses as a group:
| Declaration | Reads as |
|---|---|
const char *s |
s is a pointer to a char that can't be changed through s |
char *const s |
s is a pointer that can't be moved, to a changeable char |
int *a[10] |
a is an array of 10 pointers to int |
int (*a)[10] |
a is a pointer to an array of 10 ints |
int (*f)(int) |
f is a pointer to a function that takes an int and returns an int |
When * means multiply
The compiler decides from context. a * b multiplies, *p dereferences, and a * *p does both. One trap is purely about spacing:
int y = x/*p; // meant: x divided by *p
/* always opens a comment, so everything up to the next */ disappears, and the compiler reports something like "unterminated /* comment" or an error several lines later. Put spaces around the division operator whenever the right side starts with a dereference: x / *p.
C++ additions
C++ keeps all of this and adds a few uses: p->member is shorthand for (*p).member, smart pointers such as std::unique_ptr overload * so they dereference like raw pointers, and this is a pointer, so *this is the current object. References (int &r = x;) cover many jobs that needed pointers in C.