Const and Pointer Qualifier Combinations in Embedded C
- Ashok Kumar Kumawat
- Feb 22
- 3 min read
1. Concept Overview
There are two main dimensions when combining const with pointers:
Pointer to const integer → The integer value cannot be modified through this pointer, but the pointer itself can point elsewhere.
Const pointer to integer → The pointer itself cannot change (fixed address), but the integer value it points to can be modified.
Const pointer to const integer → Neither the pointer nor the value can be modified.
2. Embedded Industry Usage
Pointer to const integer (const int *p):
Used when accessing read-only data (e.g., calibration tables in Flash).
Prevents accidental modification of data.
Const pointer to integer (int * const p):
Used when a pointer must always point to the same memory location (e.g., fixed hardware register).
Const pointer to const integer (const int * const p):
Used for read-only hardware registers where both the address and the value must not be changed by software.
3. Practical Examples
Example 1: Pointer to const integer
const uint16_t lookup_table[5] = {10, 20, 30, 40, 50};
const uint16_t p = lookup_table;
uint16_t value = p; // Allowed
*p = 100; // ❌ Error: cannot modify through pointer
p++; // ✅ Pointer can moveExample 2: Const pointer to integer
uint16_t counter = 0;
uint16_t const p = &counter;
p = 10; // ✅ Can modify value
p++; // ❌ Error: pointer itself cannot changeExample 3: Const pointer to const integer
const uint16_t status_reg = 0x1234;
const uint16_t const p = &status_reg;
uint16_t val = p; // ✅ Can read
*p = 0xFFFF; // ❌ Error: cannot modify value
p++; // ❌ Error: pointer cannot change4. Best Practices
Use const int * when passing arrays or lookup tables to functions to ensure they aren’t modified.
Use int * const for fixed hardware register addresses.
Use const int * const for read-only hardware registers (status flags, sensor outputs).
Always combine with volatile for hardware registers that change asynchronously:
const volatile int * const p → read-only hardware register at fixed address.
MISRA-C: Explicitly declare constness to prevent unintended writes.
5. When to Use
Pointer to const (const int *):
Function parameters where data must not be modified.
Const pointer (int * const):
Fixed memory-mapped register addresses.
Const pointer to const (const int * const):
Read-only hardware registers.
Volatile combinations:
volatile int * const → fixed hardware register that can change.
const volatile int * const → fixed, read-only hardware register that changes asynchronously.
6. Memory & Performance Considerations
Const correctness: Helps compiler optimize by placing data in Flash/ROM.
Volatile correctness: Prevents compiler from caching values → ensures correct hardware access.
Pointer immutability: Reduces risk of accidental pointer reassignment.
Embedded impact:
Using const saves RAM.
Using volatile ensures correctness but may reduce speed.
Combining both ensures safety in hardware register access.
👉 Quick summary table for clarity:
Declaration | Meaning | Embedded Use Case |
const int *p | Pointer to const int (value immutable, pointer mutable) | Lookup tables, Flash data |
int * const p | Const pointer to int (pointer immutable, value mutable) | Fixed hardware register address |
const int * const p | Const pointer to const int (both immutable) | Read-only hardware register |
volatile int * const p | Const pointer to volatile int | Fixed hardware registers that changes |
const volatile int * const p | Const pointer to const volatile int | Read-only hardware registers that changes asynchronously |
Comments