Skip to content

[中文版本](/posts/c教程/zh-CN/08-调试测试与 profiling/077-Common Undefined Behaviors)

C gives you freedom, but also responsibility. Unlike higher-level languages, C doesn’t protect you from dangerous mistakes. Some actions cause undefined behavior (UB): the compiler is allowed to do anything in response, crash, hang, or even appear to work fine until it doesn’t.

Undefined behavior is what makes C both powerful and perilous. Let’s explore what causes it, how to recognize it, and how to write code that never falls into its traps.

Step 1. What Is Undefined Behavior? ​

In the C standard, undefined behavior means “no rules apply.” If your program does something the language doesn’t define, the compiler can assume it never happens and optimize freely.

This means your program might:

  • Crash immediately.
  • Produce wrong results.
  • Behave differently each time.
  • Work fine on one compiler and fail on another.

Example:

int x = 5 / 0;  // UB: division by zero

The compiler is not required to warn you or handle this safely.

Step 2. Common Sources of UB ​

Here are the most frequent offenders every C programmer must know:

CategoryExample
Out-of-bounds accessarr[10] when the array has 10 elements
Use of uninitialized variableint x; printf("%d", x);
Dangling pointer accessUse memory afterfree()
Invalid pointer arithmetic(p + 5) whenp doesn’t point into an array
Signed integer overflowint x = INT_MAX + 1;
Modifying and reading same variablei = i++; ora[i] = i++;
Null pointer dereferenceint *p = NULL; *p = 5;
Incorrect type punningAccessing a float as int through wrong pointer type
Mismatchedmalloc/freefree() memory not allocated bymalloc()
Violatingconst orvolatile contractsWriting to aconst variable

Step 3. Out-of-Bounds Access ​

int arr[3] = {1, 2, 3};
printf("%d\n", arr[3]);  // UB: index 3 is past the end

C doesn’t check bounds, you’re responsible for it. You might print garbage, crash, or accidentally overwrite another variable.

Always check:

if (index >= 0 && index < size)
    printf("%d\n", arr[index]);

Step 4. Using Uninitialized Variables ​

int x;
printf("%d\n", x);  // UB: x is uninitialized

Even if it prints0, that’s luck, not correctness. Always initialize your variables explicitly:

int x = 0;

Step 5. Dangling Pointers ​

int *p = malloc(sizeof(int));
*p = 10;
free(p);
printf("%d\n", *p);  // UB: accessing freed memory

Afterfree(), the pointer still exists but the memory doesn’t belong to you. Set it toNULL:

free(p);
p = NULL;

Step 6. Signed Integer Overflow ​

In C, signed overflow is undefined, but unsigned overflow wraps around predictably.

int x = 2147483647;
x = x + 1;  // UB

Unsigned version:

unsigned int x = 4294967295;
x = x + 1;  // wraps to 0 (defined)

To check overflow safely:

if (a > INT_MAX - b) {
    printf("overflow\n");
} else {
    x = a + b;
}

Step 7. Modifying and Reading in One Expression ​

int i = 0;
i = i++ + 1;  // UB: reading and writing i without sequence point

Avoid combining side effects. Write clean code:

i++;
i = i + 1;

Step 8. Null Pointer Dereference ​

int *p = NULL;
*p = 10;  // UB

Always validate pointers:

if (p != NULL) *p = 10;

Step 9. Type Punning and Aliasing ​

float f = 3.14;
int *ip = (int *)&f;  // UB: violates strict aliasing
printf("%d\n", *ip);

If you must reinterpret bytes, usememcpy:

int i;
memcpy(&i, &f, sizeof(i));

This avoids aliasing violations and is safe across compilers.

Step 10. Tiny Code: Detecting UB with Tools ​

Use compilers and runtime checkers to detect UB before it hits production.

gcc -fsanitize=undefined -g ub_example.c -o ub_example
./ub_example

Sample program:

#include <stdio.h>
int main(void) {
    int x = 2147483647;
    x++;
    printf("%d\n", x);
}

Output:

runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'

This is the Undefined Behavior Sanitizer (UBSan) in action, your best friend for finding invisible bugs.

Why It Matters ​

Undefined behavior is silent corruption. It can:

  • Work on your machine but fail elsewhere.
  • Break when you change compiler flags.
  • Cause subtle, unpredictable bugs.

Avoiding UB is the foundation of reliable systems programming. In C, correctness comes from discipline.

Try It Yourself ​

  1. Write a small program with deliberate UB (like using uninitialized variables).
  2. Run it with-fsanitize=undefined.
  3. Fix each issue until UBSan runs clean.
  4. Check array access and pointer validity.
  5. Refactor old C code to avoid UB, it’s a great debugging exercise.

Next, you’ll learn how to perform crash analysis and read core dumps, so even when your program fails, you can find out exactly why.

本站总访问 — 次