const pointers
Learn how const interacts with pointers in C — the distinction between pointer to const data, const pointer, and const pointer to const data.
- Distinguish between a pointer to const data and a const pointer
- Write function parameters using const correctly to indicate read-only access
- Explain what each of the four const/pointer combinations means
const in C is a promise to the compiler: "I will not modify this value." When combined with pointers, const can apply to the pointer itself, to the data it points to, or to both. The placement of const in a declaration determines which interpretation applies.
Four combinations
| Declaration | What it means |
|---|---|
int *p | Pointer to int — can change pointer and data |
const int *p | Pointer to const int — can change pointer, not data |
int * const p | Const pointer to int — can change data, not pointer |
const int * const p | Const pointer to const int — can change neither |
The rule: const applies to whatever is immediately to its left. If nothing is to the left, it applies to what is immediately to the right.
Pointer to const data
int x = 10;
const int *p = &x;
/* *p = 20; */ /* ERROR: cannot modify the value through p */
p = &x; /* OK: can point p elsewhere */const int *p — the data pointed to is const (from p's perspective). You cannot write through p, but you can point p at a different address.
This is the correct type for a function that reads but does not modify its input:
void print_array(const int *arr, int n) {
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
/* arr[0] = 0; */ /* ERROR: const -- compiler catches this */
}Using const int * for read-only parameters is not just good style — it allows callers to pass const arrays without a warning, and it documents the contract: this function will not modify your data.
Const pointer (the pointer itself is const)
int x = 10;
int * const p = &x;
*p = 20; /* OK: can modify the data */
/* p = &y; */ /* ERROR: cannot change where p points */int * const p — the pointer p is const. You cannot reassign p to point elsewhere, but you can modify the data at the address it holds. This is common for a pointer that should always refer to the same object.
Const pointer to const data
const int value = 42;
const int * const p = &value;
/* *p = 0; */ /* ERROR: data is const */
/* p = NULL; */ /* ERROR: pointer is const */A fully immutable handle — you can neither change the pointer nor write through it. Useful for truly fixed configuration data.
String literals and const
This is why string literals should be held by const char *:
const char *greeting = "Hello"; /* CORRECT */
char *mutable = "Hello"; /* compiles, but dangerous */
/* greeting[0] = 'J'; */ /* ERROR -- compiler catches it */
mutable[0] = 'J'; /* compiles, undefined behaviour at runtime */"Hello" lives in read-only memory. A const char * pointer makes the compiler enforce the read-only contract. A char * pointer allows modification attempts that crash at runtime.
Practical guideline
When writing functions, apply this rule of thumb:
- If the function only reads through a pointer parameter, declare it
const T *. - If the function must modify the data, declare it
T *. - Use
T * constwhen a local variable must always point to the same location.
int compare_strings(const char *a, const char *b) {
while (*a && (*a == *b)) {
a++;
b++;
}
return *(unsigned char *)a - *(unsigned char *)b;
}Both a and b are const char * — the function only reads the strings.
const is not a runtime guarantee. A determined programmer can cast away const with (int *)p and then write through it — this is undefined behaviour but the compiler may allow it. const is a static tool that helps the compiler catch mistakes; it is not an enforcement mechanism. The contract it documents matters more than its ability to prevent determined abuse.
Where to go next
Next: common pointer bugs — the concrete mistakes that cause crashes and security vulnerabilities, and how to recognise each one.
Pointers to pointers
Work with double pointers (int **) in C — understanding argv, functions that modify pointer variables, and multi-level indirection.
Common pointer bugs
Identify and understand the most dangerous pointer bugs in C — null dereference, dangling pointers, double free, wild pointers, and off-by-one out-of-bounds accesses.