A pointer is a variable that stores a memory address rather than a value directly — arguably the single most important and most feared concept in C.
int age = 25;
int *ptr = &age; // ptr holds the address of age
printf("%d\n", age); // 25 — the value
printf("%p\n", (void*)&age); // address of age, e.g. 0x7ffee3a1c
printf("%p\n", (void*)ptr); // same address, stored in ptr
printf("%d\n", *ptr); // 25 — dereferencing: follow the pointer to its value
*ptr = 30; // changes age itself, through the pointer
printf("%d\n", age); // 30
The & (address-of) operator gets a variable's memory address; the * operator, used two ways, either declares a pointer type (int *ptr) or dereferences an existing pointer to access the value it points to (*ptr). Since *ptr = 30 writes through the pointer directly into age's memory, this is exactly how functions can modify a caller's variables despite C's pass-by-value rule — pass a pointer to the variable, and the function dereferences it to modify the original.
An uninitialized or invalid pointer is called a wild pointer and must never be dereferenced — doing so invokes undefined behavior, commonly crashing the program. A pointer that intentionally points to nothing should be set to NULL (or, in C23, the new dedicated nullptr keyword, which is more type-safe than NULL), and should always be checked before being dereferenced.