CDeclaration & Dereferencing

Declaration & Dereferencing

Working with pointers day to day comes down to three operators: declaring a pointer variable, using & to find an address, and using * to follow (dereference) that address back to a value. This page walks through all three carefully, because the two different jobs that * does are one of the most common early stumbling blocks in C.

Declaring a pointer

A pointer declaration looks like a normal variable declaration with an extra * in front of the name:

C
int *p;      // p is a pointer to an int
double *dp;  // dp is a pointer to a double
char *cp;    // cp is a pointer to a char

The type before the * (int, double, char, ...) is the type of thing the pointer will point to — it tells the compiler how many bytes to read or write when the pointer is dereferenced, and how to interpret those bytes. p itself is not an int; it is a variable that holds the address of an int.

The dual meaning of *
This is the single most common early confusion with pointers: `*` means two completely different things depending on where it appears. In a **declaration**, `int *p;`, the `*` says "`p` is a pointer" — it is part of the type, not an operation. In an **expression**, `*p`, the `*` is the dereference operator — it says "go to the address stored in `p` and give me the value there." Same symbol, unrelated meaning, and the only way through it is practice until it becomes automatic.
The address-of operator, &

To make a pointer actually point at something, you need that something's address. The & operator, applied to a variable, produces its address:

C
#include <stdio.h>

int main(void) {
    int score = 95;
    int *p = &score;   // p now holds the address of score

    printf("score = %d\n", score);
    printf("&score = %p\n", (void *)&score);
    printf("p      = %p\n", (void *)p);
    return 0;
}
score = 95
&score = 0x7ffe4a2b3c1c
p      = 0x7ffe4a2b3c1c

Notice that p and &score print the same address — that is exactly what it means for p to "point to" score.

Dereferencing with *

Once a pointer holds a valid address, *p lets you read or write the value stored there, as if you had used the original variable's name directly:

C
#include <stdio.h>

int main(void) {
    int score = 95;
    int *p = &score;

    printf("*p before = %d\n", *p);   // reads score through p

    *p = 100;   // writes through p, which changes score
    printf("score after *p = 100: %d\n", score);

    return 0;
}
*p before = 95
score after *p = 100: 100

*p = 100; does not change what p points to — it changes the value stored at the address p already holds. That is the whole mechanism that lets a function modify a caller's variable through a pointer parameter, which is covered in detail later in this section.

Uninitialized pointers are dangerous
Wild pointers
A pointer that is declared but never given a valid address, like `int *p;` with nothing else, does not point at nothing — it holds whatever garbage bit pattern happened to be sitting in that memory when the variable was created. Dereferencing a pointer like this is called a "wild pointer" and is undefined behavior: it might crash immediately, silently corrupt unrelated memory, or appear to work by accident and fail later. Always initialize a pointer to either a valid address or to `NULL` before using it, and check for `NULL` before dereferencing (covered on the NULL & Dangling Pointers page).

C
int *p;        // WRONG: p holds garbage, do not dereference it
int *q = NULL; // OK: q clearly points to nothing yet
int value = 42;
int *r = &value; // OK: r points to a real, valid address
Putting it together
  • int *p; declares — * here is part of the type, meaning "pointer to int".

  • &x takes the address of x.

  • *p dereferences p — follows the address to read or write the value there.

  • An uninitialized pointer must be set to a real address or NULL before it is ever dereferenced.