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.