Aliasing and Strict Aliasing
This chapter explains aliasing — two pointers referring to the same object — and the strict aliasing rule that restricts which types may alias. This is the rule that makes many pointer-cast tricks undefined behavior.
Why This Matters
Strict aliasing is not a compiler whim; it is what enables aggressive optimization. When you violate it, the compiler may produce code that does something completely different from what you expect, because it assumed your two pointers could not alias.
Prerequisites
c.object.1— effective type.c.core.25— const/volatile/restrict.
Core Concept
Aliasing
Two lvalues alias when they designate the same object (or overlapping objects). For example:
int x = 5;
int *a = &x;
int *b = &x; /* a and b alias */
The strict aliasing rule
An object may be accessed only through an lvalue of one of these types:
1. a type compatible with the object's effective type; 2. a qualified version of a compatible type; 3. the signed/unsigned type corresponding to the effective type; 4. an aggregate/union type that contains one of the above among its members (with restrictions); 5. a character type (char, signed char, unsigned char).
Accessing through any other type is undefined behavior. VERIFIED
The character-type exception is why you can always inspect raw bytes with unsigned char or memcpy.
Examples
Legal: same type
int x = 5;
int *p = &x;
int y = *p; /* ok: int lvalue */
Legal: signed/unsigned correspondence
unsigned int u = 5;
int *p = (int *)&u;
int v = *p; /* ok: int and unsigned int correspond */
Illegal: unrelated types
float f = 1.0f;
int *p = (int *)&f;
int x = *p; /* UNDEFINED BEHAVIOR: int is not an allowed alias for float */
The character-type exception
unsigned char *bytes = (unsigned char *)&f;
unsigned char first = bytes[0]; /* ok: inspecting representation */
How It Works
The compiler performs alias analysis to determine whether two memory accesses can refer to the same location. If they cannot (per the strict aliasing rule), it may reorder them, cache values in registers, or vectorize loops. When you violate the rule, the compiler's analysis is wrong, and the generated code may not do what the source "obviously" does.
Variations
restrict as a stronger promise
restrict (c.ptr.restrict) tells the compiler a pointer does not alias any other pointer used to access the object, enabling even more optimization than strict aliasing alone.
Unions and aliasing
Reading a union member other than the active one is generally not a well-defined way to alias types in C17; prefer memcpy (see c.union.punning).
Common Mistakes
- Casting a pointer to an unrelated type to "reinterpret" data.
- Assuming strict aliasing violations are harmless because "the bits are the
same."
- Using unions for portable type punning.
- Forgetting that
char/unsigned characcess is the allowed escape hatch.
Undefined Behavior
- Accessing an object through an lvalue of an incompatible type (outside the
allowed list). VERIFIED
- Writing through a
const-qualified lvalue (modifying a genuinely const
object). VERIFIED
Portability
- The strict aliasing rule is standard C (all versions since C89, refined in
later standards). The exact list of allowed types is standard.
- The
signed/unsignedcorrespondence is allowed.
Under the Hood
Alias analysis feeds optimization passes such as instruction scheduling, dead-store elimination, and auto-vectorization. The -fstrict-aliasing flag (GCC/Clang) is on by default at -O2; -fno-strict-aliasing disables the assumption (used by some legacy codebases that violate the rule).
Practical Usage
- Access objects only through their declared/effective type or an allowed
alias.
- Use
memcpyto reinterpret representation portably. - Use
restrictfor performance when you can guarantee no aliasing. - Compile with
-Wstrict-aliasing(GCC/Clang) to catch some violations.
Exercises
1. Write a program that reads an int as an unsigned int (legal) and as a float (illegal); run the illegal one under UBSan. 2. Demonstrate the character-type exception by inspecting a struct's bytes. 3. Reinterpret a float as uint32_t using memcpy and compare with a pointer cast. 4. Explain how restrict differs from the strict aliasing rule.
Deep Challenge
Explain, using strict aliasing and effective type, why this common "fast float-to-int bits" idiom is UB and provide a well-defined replacement:
float f = 1.0f;
uint32_t bits = *(uint32_t *)&f; /* ? */
Then discuss how memcpy (or C23's bit_cast-style approach) solves it.
Related Concepts
c.obj.effective-type— effective type.c.ptr.restrict— restrict.c.union.punning— union type punning.c.opt.2— UB-based optimization.
References
- ISO/IEC 9899:2018 §6.5 (expressions, effective type and aliasing).
Verification
- The allowed aliasing types list is standard.
VERIFIED - Unrelated-type access is UB.
VERIFIED signed/unsignedcorrespondence is allowed.VERIFIED- No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] Aliasing definition
- [ ] Strict aliasing rule
- [ ] Allowed alias types
- [ ] Character-type exception
- [ ] signed/unsigned correspondence
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.obj.aliasing | 0 | 7 |