Introduction
A pointer is a value that holds the address of an object (or function), together with the type of what it points to. Pointers let a function modify its caller's variables, let programs build linked structures such as linked lists and trees, give access to memory obtained at run time with malloc, and underlie the way C handles arrays and strings.
Three rules carry most of this note: what & and * do, how pointer arithmetic is scaled, and how *, ++ and const combine.
Conventions: C11, LP64 (every object pointer is 8 bytes; int is 4 bytes). Addresses in examples are illustrative and written in decimal unless marked hexadecimal.
Prerequisites
- C Program Structure and Data Types: types and
sizeof. - Operators, Precedence and Side Effects: precedence of unary operators, postfix
++.
Core concepts
Addresses, & and *
Every object occupies one or more bytes, and every byte has an address. For an object a:
&a(address-of) yields a pointer toa. Its type is "pointer to the type ofa".- If
ppoints toa, then*p(indirection, dereference) designatesaitself: it is an lvalue, so*p = 15stores intoa. &and*are inverses:*&aisa, and&*pisp.
int a = 10;
int *p = &a; /* p holds the address of a */
int **q = &p; /* q holds the address of p */
*p = 15; /* a is now 15 */
**q = 20; /* *q is p, **q is a: a is now 20 */In a declaration, * belongs to the declarator, not to the type: int *p, q; declares a pointer p and an int q. Write int *p, *q; for two pointers.

*q follows one arrow, **q follows two.Pointer types and sizeof
The pointed-to type tells the compiler how many bytes to read on dereference and how far to step in arithmetic. All object pointers on LP64 are 8 bytes: sizeof(int *), sizeof(char *) and sizeof(int **) are all 8, while sizeof *p for int *p is 4.
The null pointer and void *
- A null pointer compares unequal to the address of every object. The integer constant 0 and the macro
NULLboth convert to a null pointer, sop == NULL,p == 0and!pall test for it. Dereferencing a null pointer is undefined behaviour. void *is a generic object pointer. Any object pointer converts tovoid *and back without loss, implicitly in C (no cast needed):void *v = &n; int *ip = v;. Avoid *cannot be dereferenced or used in arithmetic (gcc allows arithmetic as an extension; standard C does not).
Pointer arithmetic
For a pointer p to an element of an array of type T:
So if an int array starts at address 1000, p + 3 points to address 1012; for char it would be 1003 and for double 1024. The allowed operations are:
| Operation | Result | Condition |
|---|---|---|
p + k, p - k, p++, p-- | pointer | result must stay within the same array or one past its end |
p - q | ptrdiff_t, number of elements between them | same array or one-past, and the mathematical difference is representable in ptrdiff_t |
p < q, p == q and so on | 0 or 1 | relational: only C11-defined object/aggregate cases; equality: valid operand types required, but not restricted to one array |
p + q, p * 2, p / q | not allowed | does not compile |
p[k] is defined as *(p + k); this is the link between pointers and arrays (Arrays and Their Relation to Pointers).
Combining * with ++
Postfix ++ binds tighter than unary *; prefix ++ and * are both unary and group right to left.
| Expression | Meaning | Effect |
|---|---|---|
*p++ | *(p++) | yields the value at the old p, then p moves to the next element |
(*p)++ | increment the pointee | yields the old pointee value; p unchanged |
++*p | ++(*p) | increments the pointee, yields the new value |
*++p | *(++p) | moves p first, then yields the new pointee |
const and pointers
Read declarations from the name outward: const applies to what is immediately to its left, or to the right if nothing is on its left.
| Declaration | *p = 5 | p = &b | Read as |
|---|---|---|---|
const int *p (same as int const *p) | error | allowed | pointer to const int |
int *const p = &a | allowed | error | const pointer to int |
const int *const p = &a | error | error | const pointer to const int |
A pointer to const only promises that the object will not be modified through that pointer; the object itself may still change by other means.
Dangerous pointers
- Uninitialised (wild) pointer: an automatic
int *p;holds an indeterminate value;*p = 1is undefined behaviour. - Dangling pointer: points to an object whose lifetime has ended, such as a local variable of a function that has returned, or a block that has been
freed. - Null pointer: valid to hold and compare, invalid to dereference.
How it works: pointers as function arguments
C passes arguments by value (Functions and Parameter Passing), so a pointer parameter is a copy of the caller's pointer. Through the copy the function can change the pointee; it cannot change the caller's pointer variable. To change a pointer, pass the address of the pointer:
void wrong_set(int *p, int *target) { p = target; } /* changes the copy */
void set_to(int **pp, int *target) { *pp = target; } /* changes the caller's p */
int a = 1, b = 2, *p = &a;
wrong_set(p, &b); /* *p is still 1 */
set_to(&p, &b); /* *p is now 2 */This is exactly why linked-list insertion at the head takes struct node **head.
Worked examples
All outputs below are for C11 with gcc on x86-64 Linux (LP64).
Example 1 (levels of indirection). With int a = 10; int *p = &a; int **q = &p;: *p, **q and *p + 1 are 10, 10 and 11. After **q = 20, a is 20. After int b = 99; *q = &b;, p points to b, so *p is 99 while a is still 20.
Example 2 (scaling). int arr[5] = {10, 20, 30, 40, 50}; int *p = arr; Then *(p + 2) is 30, p[4] is 50, *(arr + 1) is 20. p + 3 is 12 bytes past p, a char * advanced by 3 is 3 bytes past, and a double * advanced by 3 is 24 bytes past. With int *r = &arr[4];, r - p is 4 (elements, not bytes).
Example 3 (the four */++ forms). Start with int a[4] = {10, 20, 30, 40}; int *p = a; and run in order:
| Statement | x | Where p points | Array afterwards |
|---|---|---|---|
x = *p++; | 10 | a[1] | 10 20 30 40 |
x = (*p)++; | 20 | a[1] | 10 21 30 40 |
x = ++*p; | 22 | a[1] | 10 22 30 40 |
x = *++p; | 30 | a[2] | 10 22 30 40 |
Example 4 (const). const int *p = &a; *p = 5; fails with "assignment of read-only location". int *const p = &a; p = &b; fails with "assignment of read-only variable".
Equality operand types. For pointer equality, use pointers to compatible (possibly differently qualified) types, an object/incomplete pointer paired with a suitably qualified void *, or a pointer paired with a null pointer constant, as permitted by C11 6.5.9. “Different arrays” does not by itself forbid a validly typed equality test. Relational comparisons follow the separate array/structure/union cases of 6.5.8.
Important properties and observations
int *p, q;makes onlypa pointer.- Pointer arithmetic counts in elements of the pointed-to type;
p - qis in elements. p[i]is*(p + i), and alsoi[p], since addition commutes.- All object pointers have the same size on common systems (8 bytes on LP64), whatever they point to.
- A pointer may legally point one past the end of an array, but must not be dereferenced there.
void *converts implicitly to and from other object pointer types in C.- Changing a caller's pointer needs a pointer to that pointer.
GATE examination perspective
- Multi-level indirection: draw boxes and arrows; each
*follows one arrow. - Address arithmetic with a given base address: multiply by the element size the question states.
*p++family: use the table above; the usual pitfall is treating*p++as incrementing the pointee.constplacement questions: which assignments compile.- Swap and pointer-to-pointer questions: decide whether the caller's variable, pointer or pointee changes.
Summary
Sources
- ISO/IEC 9899:2011 (C11), committee draft N1570, (consulted 2026-11-09), Section 6.3.2.3 (pointers, null pointer constants, void * conversions), 6.5.2.1 (array subscripting), 6.5.3.2 (address and indirection operators), 6.5.6 (additive operators, pointer arithmetic), 6.5.8 and 6.5.9 (relational and equality operators), 6.7.3 (type qualifiers), 6.7.6.1 (pointer declarators).
- B. W. Kernighan and D. M. Ritchie, The C Programming Language, 2nd ed., Prentice Hall, 1988: Chapter 5 (pointers and arrays), Sections 5.1 to 5.4.