const Qualifier and Read-Only Memory in C
The const type qualifier in C primarily serves as a
compile-time constraint that prevents code from directly modifying
specified variables. While developers often associate const
directly with physical read-only memory (ROM), its role in enforcing
read-only memory semantics is a combination of static type checking by
the compiler and, in specific contexts, architectural memory protection
provided by the operating system and hardware. This article examines how
const operates at the compiler level, where
const data is physically placed in memory, and the
limitations of its protection mechanisms.
Compile-Time Enforcement
At its core, const establishes an interface contract
enforced during compilation. When an object is declared with the
const qualifier, the compiler checks the abstract syntax
tree to ensure that the identifier does not appear as the left-hand
operand of an assignment operator, nor is it subject to increment or
decrement operations.
If code attempts to modify a const variable directly,
the compiler issues an error or warning:
const int max_limit = 100;
max_limit = 200; /* Compiler error: assignment of read-only variable */Beyond direct assignments, the compiler uses const for
type checking function parameters. Passing a pointer to
const data ensures that the function cannot modify the
referenced value through that pointer without triggering a diagnostic
message.
Physical
Memory Allocation and the .rodata Section
While const provides logical immutability at the source
level, physical enforcement depends on storage duration and the target
platform:
Global and Static Variables: When
constis applied to variables with static storage duration (global variables and variables declared withstatic), compilers typically place this data into a dedicated read-only section of the binary, commonly labeled.rodatain ELF binaries or.rdatain PE formats. When the operating system loads the program, the memory management unit (MMU) maps this section into memory pages flagged with read-only permissions. Any runtime attempt to write to these pages results in a hardware-level fault, such as a segmentation fault (SIGSEGV).Automatic (Local) Variables: When a
constvariable is declared inside a function without thestatickeyword, it possesses automatic storage duration. These variables are allocated on the runtime call stack. Because the stack must remain writable for normal program execution (pushing and popping stack frames), localconstvariables are not placed in hardware-protected read-only memory. Enforcement for local variables is strictly limited to compile-time diagnostics.
Casting and Undefined Behavior
C allows explicit type casting, which makes it possible to strip the
const qualifier from a pointer:
const int value = 42;
int *ptr = (int *)&value;
*ptr = 99; /* Undefined behavior */Modifying an object originally defined with a const type
qualifier through a non-const pointer invokes undefined
behavior according to the ISO C standard:
- If
valuewas placed in.rodata(e.g., a static or global object), the hardware memory protection will terminate the program immediately with a memory access violation. - If
valueresides on the stack, the write may physically succeed at runtime, altering the value in memory. However, because the compiler may assumevaluenever changes, it may inline the original constant42into subsequent expressions during optimization, resulting in inconsistent program state.
Embedded Systems and True ROM
In bare-metal embedded systems without an MMU or a dynamic operating
system, the const qualifier plays a direct role in memory
placement via the linker script. Global variables qualified with
const can be mapped directly to non-volatile flash memory
or ROM instead of being copied into scarce RAM during the C runtime
startup routine (crt0). In this environment,
const directly dictates hardware read-only memory
utilization, saving RAM and preventing physical writes at the bus
level.