Code of the Day
IntermediatePointers

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.

CIntermediate9 min read
Recommended first
By the end of this lesson you will be able to:
  • 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

DeclarationWhat it means
int *pPointer to int — can change pointer and data
const int *pPointer to const int — can change pointer, not data
int * const pConst pointer to int — can change data, not pointer
const int * const pConst 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 * const when 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.

Finished reading? Mark it complete to track your progress.

On this page