Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A restrict-qualified pointer is a type qualifier introduced in C99 that asserts a strict non-aliasing guarantee to the compiler. By applying restrict to a pointer, the programmer explicitly promises that for the lifetime of that pointer, if the memory object it references is modified, all accesses to that object (both reads and writes) will occur exclusively through that pointer or pointers derived directly from it.

Type Constraints

The restrict qualifier can only be applied to pointers that point to object types. Because incomplete types (such as void or forward-declared structs) are formally classified as a subset of object types in the C Standard, they are inherently supported. It is a constraint violation to apply restrict to function pointers.

Syntax and Contexts

The restrict keyword qualifies the pointer itself, not the underlying type. In standard pointer declarations, it is placed after the asterisk (*). However, when applying the qualifier to a typedef of a pointer type, it is placed directly after the type name. Additionally, C99 introduced a specific syntax for array parameters in function declarations, allowing restrict to be placed inside the array brackets.
// Standard declaration: 'ptr' is a restrict-qualified pointer to an int
int * restrict ptr; 

// Typedef declaration: The qualifier is applied directly to the type name
typedef int* IntPtr;
IntPtr restrict p;

// Array parameter syntax: Equivalent to 'int * restrict dest'
void process_memory(int dest[restrict], const int src[restrict], size_t n);

// Invalid: Cannot apply restrict to a function pointer
// void (* restrict fp)(void); 

Core Semantics and Scope

The restrict qualifier establishes a formal contract regarding pointer aliasing. The rules of this contract are bound to the pointer’s associated block (C11 6.7.3.1). The associated block is determined by how the pointer is declared or designated:
  • Block Scope and Parameters: If the pointer is declared inside a block or as a function parameter, the associated block is the block in which the declaration occurs (or the function body for parameters).
  • File Scope: If the pointer is declared at file scope (as a global), its associated block is defined by the standard as the block of main, meaning the non-aliasing promise applies to the entire execution of the program.
  • Struct Members: If restrict is applied to a member of a struct or union, the associated block is determined by the declaration of the ordinary identifier used to designate the object containing the member. (Note: As highlighted in C Defect Report 460, the standard’s formal definition is ambiguous regarding dynamically allocated objects that lack an ordinary identifier, but the semantics strictly rely on the lifetime of the identifier used to access the object).
Within the pointer’s associated block, the following rules apply:
  1. Exclusive Access on Modification: If the memory object is modified, all accesses to that object (both reads and writes) must occur through the restrict pointer.
  2. Derivation: Pointers derived from a restrict pointer (e.g., via pointer arithmetic like ptr + 1 or array indexing ptr[i]) are covered by the same guarantee and are legally allowed to access the memory.
  3. Read-Only Exception: If the memory object is never modified during the pointer’s lifetime, the restrict qualifier is effectively ignored, and multiple pointers can safely read from the same location.

Compiler Implications

The primary purpose of restrict is to enable aggressive compiler optimizations that are otherwise blocked by the possibility of aliasing. Without restrict, the compiler must assume that writing to one pointer might alter the value read by another pointer. This forces the compiler to emit defensive machine code:
  • Flushing registers to memory.
  • Reloading values from memory before subsequent reads.
  • Preserving strict, sequential instruction ordering.
By guaranteeing no aliasing, restrict permits the compiler to:
  • Cache values in registers: The compiler knows the underlying memory will not be mutated by a side channel.
  • Reorder instructions: Independent loads and stores can be scheduled optimally for the CPU pipeline.
  • Vectorize operations: The compiler can safely emit SIMD (Single Instruction, Multiple Data) instructions without emitting runtime checks for overlapping memory boundaries.

Undefined Behavior

Because restrict is a programmer-enforced promise, the compiler does not verify it at compile time. If the contract is violated—meaning a restrict-qualified pointer’s target is modified, and that same memory is accessed via an independent, non-derived pointer within the pointer’s associated block—the program invokes Undefined Behavior (UB).
void violate_restrict(int * restrict p1, int * p2) {
    // If the caller passes the same address for p1 and p2, aliasing occurs.
    *p1 = 10;
    *p2 = 20; // UNDEFINED BEHAVIOR: Modifying the object through an un-derived alias
}
In the presence of UB, the compiler’s optimizations may result in stale register reads, corrupted data, or unpredictable execution paths, as the generated machine code operates under the false assumption that p1 and p2 point to disjoint memory regions.
Master C with Deep Grasping Methodology!Learn More