Last updated: March 2025

Common Mistakes in C Programming: 16 Errors With Exact Fixes

By CoodeVerse Editorial Team ⏱ 11 min read

Every C programmer hits the same wall of mistakes. The good news: most of them produce specific, recognizable symptoms — a specific compiler error, crash pattern, or wrong output. This guide organizes the 16 most common mistakes into four categories, gives you the exact error message or symptom, shows the broken code and the fix, and tells you which compiler flag or tool catches it.

🔴
Category 1 — Structure Errors
Detected at compile time. GCC refuses to build until fixed.
Mistake 1Missing #include for a function
warning: implicit declaration of function 'printf'
Caught by: gcc -Wall (warning; error in C99+)
You called a function without including the header that declares it. In C99 and later, calling an undeclared function is an error. The error always names the function — search for the header that declares it.
❌ missing include
C
int main() {
    printf("Hi\n");  // undeclared
    return 0;
}
✅ fixed
C
#include <stdio.h>
int main() {
    printf("Hi\n");
    return 0;
}
Mistake 2void main() instead of int main()
warning: return type of 'main' is not 'int'
Caught by: gcc -Wall, -std=c11
void main() is not valid C. The OS and shell scripts need main()'s return value as the process exit code. Shell scripts test it with echo $?; CI systems use it to detect failures.
❌ non-standard
C
void main() {
    printf("Hi\n");
}
✅ standard
C
int main() {
    printf("Hi\n");
    return 0;
}
Mistake 3Missing semicolon after a statement
error: expected ';' before 'return'
Caught by: GCC at compile time
GCC detects the error one line after the actual mistake — it only knows the semicolon is missing when it sees the next unexpected token. Always check the line before the reported error line.
❌ missing ;
C
int x = 10   // ← missing ;
return 0;
✅ fixed
C
int x = 10;
return 0;
Mistake 4Mismatched or missing braces
error: expected '}' at end of input
Caught by: GCC at compile time
Every { needs a matching }. GCC reports the error at the end of the file — far from the actual mistake. Use an editor with bracket matching (VS Code highlights the mismatched brace; Vim's % key jumps between pairs).
❌ unmatched
C
int main() {
    if (1) {
        printf("hi\n");
    // ← missing }
    return 0;
}
✅ matched
C
int main() {
    if (1) {
        printf("hi\n");
    }
    return 0;
}
🟡
Category 2 — Logic Bugs
Code compiles and runs — but produces wrong output. Often silent.
Mistake 5Missing braces around if/for/while body
No error — compiles silently, wrong output
Caught by: Code review; Apple's "goto fail" SSL bug (CVE-2014-1266) is the canonical real-world example
Without braces, only the first statement is in the if-body. Any subsequent lines always run regardless of the condition. This is the "refactoring trap" — the indentation makes it look correct but it isn't.
❌ missing braces
C
if (x > 0)
    printf("pos\n");
    printf("always!\n"); // ALWAYS runs
✅ with braces
C
if (x > 0) {
    printf("pos\n");
    printf("also pos\n");
}
Mistake 6= (assignment) instead of == (comparison)
warning: suggest parentheses around assignment used as truth value
Caught by: gcc -Wall
if (x = 5) assigns 5 to x and evaluates to true (5 is non-zero), so the body always runs. Compiles without error. GCC -Wall warns. Yoda conditions if (5 == x) make a typo a compile error.
❌ assignment
C
if (x = 5) {   // assigns, not compares
    printf("always\n");
}
✅ comparison
C
if (x == 5) {
    printf("when x is 5\n");
}
Mistake 7Off-by-one error in array loops
No compile error — wrong output or silent out-of-bounds crash
Caught by: -fsanitize=address
Arrays are 0-indexed — a 5-element array has valid indices 0..4. Using i <= n instead of i < n accesses arr[n] — out-of-bounds, undefined behavior. One too many iterations; or using i < n-1 may miss the last element.
❌ off by one
C
int arr[5] = {1,2,3,4,5};
for (int i = 0; i <= 5; i++)  // accesses arr[5]!
    printf("%d\n", arr[i]);
✅ correct
C
int arr[5] = {1,2,3,4,5};
for (int i = 0; i < 5; i++)   // 0..4
    printf("%d\n", arr[i]);
Mistake 8Stray semicolon after for/while — empty loop body
warning: empty body in a for-statement [-Wempty-body]
Caught by: gcc -Wall
A semicolon immediately after a for/while is an empty statement that IS the entire loop body. The intended body runs only once after the loop terminates (or never for an infinite loop).
❌ stray ;
C
for (int i=0; i<10; i++);  // empty body!
    printf("%d\n", i);  // runs once after loop
✅ fixed
C
for (int i=0; i<10; i++) {
    printf("%d\n", i);
}
Mistake 9Using uninitialized variables
warning: 'sum' is used uninitialized
Caught by: gcc -Wall, -fsanitize=undefined
Local variables are not automatically initialized — they contain whatever garbage was in that stack memory. Reading an uninitialized variable is undefined behavior: the program may produce different results each run, or crash.
❌ uninitialized
C
int sum;
for (int i=1; i<=5; i++)
    sum += i;   // sum starts as garbage
✅ initialized
C
int sum = 0;
for (int i=1; i<=5; i++)
    sum += i;
🟣
Category 3 — Memory Errors
Compiles and may appear to run — but corrupts memory or leaks it. Often intermittent.
Mistake 10Not checking malloc's return value
Segmentation fault (when memory exhausted)
Caught by: Code review; -fsanitize=address
malloc returns NULL when the system is out of memory. Using the returned pointer without checking it causes a null pointer dereference — an immediate crash. Always validate before use.
❌ no check
C
int *p = malloc(1000 * sizeof(int));
p[0] = 42;  // crash if NULL
✅ check first
C
int *p = malloc(1000 * sizeof(int));
if (!p) { perror("malloc"); return 1; }
p[0] = 42;
Mistake 11Memory leak — malloc without free
valgrind: LEAK SUMMARY: definitely lost: N bytes in M blocks
Caught by: valgrind --leak-check=full, -fsanitize=address
Every heap allocation needs exactly one matching free. Missing free calls accumulate — in long-running programs (servers, daemons) the process eventually exhausts memory and crashes.
❌ leak
C
char *buf = malloc(256);
use(buf);
// free(buf) missing
✅ freed
C
char *buf = malloc(256);
use(buf);
free(buf);
buf = NULL;
Mistake 12Buffer overflow — writing past an array's end
AddressSanitizer: stack-buffer-overflow / heap-buffer-overflow
Caught by: -fsanitize=address, valgrind
Writing beyond a buffer corrupts adjacent memory — other variables, return addresses, heap metadata. Buffer overflows are the leading cause of C security vulnerabilities. Use bounded alternatives: strncpy, snprintf, fgets.
❌ overflow
C
char buf[8];
strcpy(buf, "Hello, World!");  // overflow!
✅ bounded
C
char buf[8];
strncpy(buf, "Hello, World!", sizeof(buf)-1);
buf[sizeof(buf)-1] = '\0';
Mistake 13Use-after-free — accessing memory after free()
AddressSanitizer: heap-use-after-free
Caught by: -fsanitize=address, valgrind
After free(ptr), the memory may be reused for another allocation. Reading or writing through the freed pointer reads/writes someone else's data — a serious bug and common security vulnerability. Setting the pointer to NULL makes subsequent accesses crash immediately (caught) rather than silently corrupting memory (uncaught).
❌ use after free
C
int *p = malloc(sizeof(int));
free(p);
*p = 42;   // undefined behavior!
✅ NULL after free
C
int *p = malloc(sizeof(int));
free(p);
p = NULL;  // next use crashes immediately
🟢
Category 4 — Runtime Errors
Program compiles, but crashes or exhibits undefined behavior at runtime.
Mistake 14NULL pointer dereference
Segmentation fault (SIGSEGV)
Caught by: -fsanitize=address (exact line), gdb backtrace
Dereferencing NULL attempts to read address 0, which the OS marks inaccessible. Most common cause: forgetting to check the return value of malloc, fopen, or scanf, all of which can return NULL.
❌ unchecked
C
FILE *fp = fopen("missing.txt", "r");
fread(buf, 1, 100, fp);  // crash if NULL
✅ checked
C
FILE *fp = fopen("file.txt", "r");
if (!fp) { perror("fopen"); return 1; }
fread(buf, 1, 100, fp);
Mistake 15Signed integer overflow — undefined behavior
No crash — silently wrong output or UB-based misoptimization
Caught by: -fsanitize=undefined
Signed integer overflow (e.g., INT_MAX + 1) is undefined behavior in C. The compiler can and does make optimizations that assume overflow never happens — leading to code that appears to work at -O0 but fails silently at -O2.
❌ overflow UB
C
int a = 2147483647;  // INT_MAX
int b = a + 1;       // undefined behavior!
printf("%d\n", b);
✅ wider type
C
long long a = 2147483647LL;
long long b = a + 1;   // 2147483648
printf("%lld\n", b);
Mistake 16Infinite recursion — stack overflow
Segmentation fault (stack exhausted)
Caught by: gdb (shows full call stack depth), -fsanitize=address
A recursive function without a reachable base case adds a stack frame every call until the stack is exhausted. Always verify that every valid input eventually reaches the base case.
❌ no base case
C
int fact(int n) {
    return n * fact(n - 1);  // infinite!
}
✅ base case
C
int fact(int n) {
    if (n <= 1) return 1;
    return n * fact(n - 1);
}

Tools: Find These Mistakes Automatically

Tool / FlagWhat it findsHow to use
gcc -Wall -WextraImplicit declarations, unused vars, format mismatches, assignment in condition, missing returnsgcc -Wall -Wextra prog.c -o prog
gcc -fsanitize=addressBuffer overflows, use-after-free, double-free, stack overflows, memory leaksgcc -g -fsanitize=address prog.c -o prog && ./prog
gcc -fsanitize=undefinedSigned integer overflow, null pointer dereference, out-of-bounds indexinggcc -g -fsanitize=undefined prog.c -o prog && ./prog
valgrindMemory leaks, use-after-free, uninitialized reads, invalid reads/writesgcc -g prog.c -o prog && valgrind --leak-check=full ./prog
gdbPinpoints segfault line, shows call stack at crash, step-through debugginggcc -g prog.c -o prog && gdb ./progrun, bt
clang --analyzeNull pointer dereference, dead code, memory leaks — static, no execution neededclang --analyze prog.c
Recommended one-liner for development:
gcc -Wall -Wextra -Werror -g -fsanitize=address -fsanitize=undefined -std=c11 prog.c -o prog && ./prog
This single command catches all four categories of mistakes simultaneously.

Frequently Asked Questions

The five most common beginner C mistakes are: (1) missing #include for functions, (2) omitting braces around if/for/while bodies, (3) using uninitialized variables, (4) not checking malloc/fopen return values, and (5) array off-by-one errors. All five are caught by compiling with -Wall -fsanitize=address.
A segfault means your program accessed invalid memory. Common causes: NULL pointer dereference (forgot to check malloc/fopen), array out-of-bounds, use-after-free, or stack overflow from infinite recursion. Diagnose with: gcc -g -fsanitize=address prog.c -o prog && ./prog — AddressSanitizer prints the exact line and cause.
Common causes: = instead of == in an if condition (always true), missing braces so only one statement is conditional, off-by-one loop bounds, uninitialized variable (garbage value), or integer overflow. Compile with -Wall -fsanitize=undefined to catch most of these.
Undefined behavior (UB) means the C standard makes no guarantee — the program might crash, produce wrong output, or appear to work but fail after a compiler update or optimization level change. Common causes: signed integer overflow, reading uninitialized variables, NULL dereference, array out-of-bounds, use-after-free. Catch with -fsanitize=undefined.
Two tools: (1) valgrind --leak-check=full ./prog — exhaustive, reports exact allocation sites, ~10–20x slower. (2) gcc -fsanitize=address prog.c -o prog && ./prog — AddressSanitizer, ~2x overhead, reports leaks at exit with source locations. Use ASan for daily development; valgrind for final pre-release checks.

Continue learning C on CoodeVerse

CoodeVerse Editorial Team

The CoodeVerse editorial team consists of experienced software developers and educators specializing in C, Python, Java, and web development. All content is technically reviewed and updated regularly.