C Mastery / Indeterminate Values and Trap Representations
Part 2 — The Object Model and Undefined Behavior

Indeterminate Values and Trap Representations

This chapter covers two low-level concepts that explain why reading uninitialized variables is dangerous: indeterminate values and trap representations.

Why This Matters

Reading an uninitialized variable is one of the most common C bugs. Its exact status — undefined behavior or unspecified value — depends on the type and whether the type has trap representations. Understanding this prevents "it worked in my test" bugs.

Prerequisites

Core Concept

Indeterminate values

An object with automatic storage duration that is not initialized has an indeterminate value (for types without a guaranteed default). Reading an indeterminate value has special rules:

value (not UB).

behavior, especially if the type has trap representations. In C17, reading an indeterminate value of a type that can hold a trap representation (e.g., some implementations for int) is UB; if no trap representation exists, it yields an unspecified value. VERIFIED

Trap representations

A trap representation is a bit pattern that does not represent a valid value of the type and, if used as a value, causes undefined behavior (often a hardware trap). Not all types have trap representations. On mainstream two's-complement machines, integer types have no trap representations, but floating-point types may, and padding bits (pre-C23) could create them.

Syntax

int x;            /* indeterminate if automatic and uninitialized */
unsigned char c;  /* indeterminate, but reading is unspecified (not UB) */

Examples

Uninitialized automatic int

#include <stdio.h>

int main(void)
{
    int x;              /* indeterminate */
    printf("%d\n", x);  /* may be UB or unspecified; never rely on it */
    return 0;
}

This program's behavior is not portable. It may print garbage, may crash, or may be optimized unpredictably.

Uninitialized unsigned char

#include <stdio.h>

int main(void)
{
    unsigned char c;   /* indeterminate */
    printf("%u\n", c); /* unspecified value, but not UB */
    return 0;
}

Reading an unsigned char is always at least "unspecified value," never UB from the value itself.

Static storage is zero-initialized

#include <stdio.h>

int global;          /* zero-initialized (not indeterminate) */
int main(void)
{
    static int s;    /* zero-initialized */
    printf("%d %d\n", global, s);   /* 0 0 */
    return 0;
}

Objects with static storage duration are always initialized (to zero if no explicit initializer), so they are never indeterminate.

How It Works

Automatic objects are allocated on the stack and not automatically initialized. Their initial bytes are whatever happened to be in that memory. The compiler may track that a value is indeterminate and exploit that in optimization (for example, assuming a branch reading it is UB and removing it).

Variations

malloc'd memory

malloc returns storage with *indeterminate* contents. calloc zeroes the storage (all bytes zero), but be aware that all-bits-zero is not necessarily a valid value for all types on all implementations (for integers it is; for pointers it is a null pointer; for floating-point it is +0.0 in IEEE 754).

Padding bytes

Reading padding bytes of a struct (via unsigned char and memcpy) yields unspecified values; they are not guaranteed to be zero.

Common Mistakes

Undefined Behavior

UB. VERIFIED

Portability

unsigned char is always unspecified (not UB). VERIFIED

Under the Hood

The compiler may use "indeterminate value" as a license to assume a read never happens legitimately and optimize accordingly (e.g., remove the read or assume a branch is unreachable). This is a real-world source of surprising behavior under optimization.

Practical Usage

the result as unspecified.

Exercises

1. Write a program that reads an uninitialized int and run it with and without optimization; observe the difference. 2. Demonstrate that unsigned char uninitialized reads are at least not UB. 3. Show that static and global variables are zero-initialized. 4. Explain why calloc is safer than malloc for zero-initialized data.

Deep Challenge

Using the C standard's definitions, explain the precise difference between an "indeterminate value" and an "unspecified value," and why reading an indeterminate int can be UB while reading an indeterminate unsigned char is not. Cite the relevant rule category.

References

§6.2.6.1 (object representation), §6.7.9 (initialization).

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.obj.indeterminate06
c.obj.trap06