C-Specific Pitfalls in studies

Most study problems are language-neutral in statement, but C imposes its own layer of correctness concerns — integer overflow, char* vs char[], size_t vs int for indices, and the discipline of malloc/free under time pressure. This file inventories the traps that cost people points in real studies, so you can spot them before they bite.

Trap 1: Integer Overflow in DP and Math

The single most common bug in C study solutions. The study partner sees you write int dp[100][100] and knows what’s coming.

Where It Bites

  • Counting DPs: “count number of ways to X” — the count grows exponentially. Overflows int around problem sizes as small as N=30. Use long long and take mod 10^9 + 7 when the problem asks for it.

  • Sum of large ranges: Sum of N up to 10^6 with values up to 10^9 overflows int. Use long long.

  • Product of two ints: a * b where a and b are each near INT_MAX. Cast first: (long long)a * b.

  • Binary search midpoint: (lo + hi) / 2 overflows when both are large. Use lo + (hi - lo) / 2.

  • Sentinel values in DP: INT_MAX as “infinity” and then you do dp[i] + 1 — overflows to INT_MIN. Use INT_MAX / 2 or INT_MAX - 1000 as a safer sentinel.

The Rule

Default to long long for any accumulator or DP value unless you’re certain of bounds. Convert back to int at the return boundary. The cost of long long on a 64-bit machine is essentially nil.

long long dp[n+1];
// ... compute ...
return (int)(dp[n] % <phone_number_or_numberic_id_or_random_id_12>);

Trap 2: size_t vs int for Indices

size_t is unsigned, int is signed. Mixing them is legal but full of traps.

The Wraparound Bug

for (size_t i = n - 1; i >= 0; i--) { /* ... */ }   // INFINITE LOOP

size_t never goes below 0; it wraps to SIZE_MAX. Two fixes:

for (size_t i = n; i-- > 0; ) { /* use i */ }   // safe idiom
for (int i = (int)n - 1; i >= 0; i--) { /* ... */ }   // cast to int

The Signed-Unsigned Comparison Warning

int i;
size_t n = strlen(s);
for (i = 0; i < n; i++) { /* comparison warning: -Wsign-compare */ }

The implicit conversion promotes i to size_t, which is fine if i is nonnegative but scary in general. Just use the same type on both sides.

The Rule

  • Use int for indices in study code. Simpler, no wraparound, matches LC’s function signatures.

  • Use size_t for libprep public APIs (matching strlen, sizeof conventions), but internally use int when you’re doing arithmetic that might go negative.

  • Never mix them in a single expression without an explicit cast.

Trap 3: char* vs char[] — The Modification Trap

char *s = "hello";   // pointer to string LITERAL in .rodata
s[0] = 'H';           // SIGSEGV — undefined behavior

char t[] = "hello";  // array on stack, initialized by copy
t[0] = 'H';           // fine

Most LC problem signatures give you a char* that IS modifiable (they allocate it), but always check. And when you return a char*, decide whether it points to caller-owned, malloc’d, or static memory and document it.

Trap 4: The free Discipline in a Timed study

Under time pressure, free calls are the first thing people forget. In C studies (not LC — real studies with a human), the study partner will absolutely check.

The Habits That Save You

  1. Every malloc gets paired with a free — write them at the same time. If you allocate at line 20, write the free at line 40 immediately, even before line 25.

  2. free-then-NULL: free(p); p = NULL; — makes double-free crash loudly instead of silently.

  3. A destroy_X function per struct that allocates: if X_new mallocs, X_free frees. Pair them.

  4. Test locally with ASan. If time permits, run -fsanitize=address before submitting.

Trap 5: strncmp Off-by-One and Null-Termination

strncmp(a, b, n) compares at most n characters. If both a and b are null-terminated within n, works fine. If neither is, works fine. If one is and the other isn’t at position n, it’s still safe.

But strncpy(dst, src, n) does NOT null-terminate if strlen(src) >= n. This is one of the C standard’s original sins.

char buf[10];
strncpy(buf, "a very long string", 10);   // buf is NOT null-terminated
printf("%s\n", buf);                        // undefined behavior

// Fix:
strncpy(buf, "a very long string", sizeof buf - 1);
buf[sizeof buf - 1] = '\0';

// Or use snprintf:
snprintf(buf, sizeof buf, "%s", "a very long string");

Rule: prefer snprintf over strncpy for building strings; use strncmp for comparison. If you must use strncpy, always follow with an explicit null-terminator.

Trap 6: Recursion Stack Overflow

Default Linux stack is 8 MB. Each recursive call is ~1 KB (locals + saved registers + arguments). ~8000 levels of recursion and you segfault.

Where It Bites

  • Recursive DFS on a linked-list-shaped graph or tree: V = 10^5 vertices, chain shape, depth 10^5, blows the stack.

  • Recursive backtracking with huge branching: usually depth-limited by the problem, so less risk, but check.

  • bst_insert(root, val) on an already-sorted input: creates a skewed BST of depth N. Insert N elements = N^2 total function calls, depth N. Both time and stack bomb.

The Fix

Rewrite iteratively with an explicit stack from libprep::stack. Uglier code, but no stack limit. See 06_graphs_bfs_dfs.md for the iterative DFS template.

Trap 7: Uninitialized Local Variables

int sum;
for (int i = 0; i < n; i++) sum += arr[i];   // sum starts at garbage

BSS globals are zero-initialized; stack locals are not. Always initialize:

int sum = 0;

Compile with -Wall -Wuninitialized and it warns. study partners watching your terminal will notice you didn’t set -Wall.

Trap 8: Array Decay in Function Parameters

void f(int arr[10]) { printf("%zu\n", sizeof arr); }   // prints 8 (or 4), NOT 40

An array parameter decays to a pointer. sizeof arr inside f is the pointer size, not the array size. Always pass the length as a separate parameter:

void f(int *arr, size_t n) { /* ... */ }

This is why every LC array function signature takes both the array and its length. Get in the habit.

Trap 9: qsort Comparator Function

qsort takes a comparator that returns int — negative, zero, or positive. Do not write return a - b; when a and b are int — their difference can overflow int:

int cmp_int(const void *a, const void *b) {
    int x = *(const int*)a, y = *(const int*)b;
    return x - y;                      // WRONG: overflow if x - y overflows
    return (x > y) - (x < y);          // CORRECT: no overflow, exactly -1/0/1
}

The (x > y) - (x < y) idiom is the safe form. Memorize it.

Trap 10: Returning Pointer to Local Variable

int *make_array(void) {
    int arr[10] = {0};
    return arr;    // UNDEFINED BEHAVIOR: arr is out of scope after return
}

Every C programmer writes this bug exactly once, watches the mysterious data corruption, and never writes it again. Return malloc’d memory and document who frees. Or take an output buffer as a parameter (void make_array(int *out)).

Trap 11: NULL vs 0 vs '\0'

All three are integer zero, but semantically:

  • NULL for pointers.

  • 0 for arithmetic.

  • '\0' for characters (specifically the null terminator).

Code that says if (ptr == 0) works, but if (!ptr) or if (ptr == NULL) reads better.

Trap 12: The ? : Precedence Landmine

int x = a > b ? a : b + 1;   // is this (a > b ? a : b) + 1 or a > b ? a : (b + 1)?

Answer: a > b ? a : (b + 1). The ternary has very low precedence. Parenthesize when in doubt — costs nothing, reads clearer:

int x = (a > b) ? a : (b + 1);

The Meta-Rule

Most of these traps have one thing in common: the compiler warns you. Compile with -Wall -Wextra -Wpedantic -fsanitize=address,undefined and half of these bugs never make it to a run. In an study, mentioning that you’d normally compile with these flags scores real points — it signals that you take C’s sharp edges seriously.

What Most People Get Wrong About This

They memorize the language spec and forget the tooling. C without -Wall -Wextra -Wpedantic -fsanitize=address is nuclear weapons without controls; C with them is a professional tool. study partners who write C for a living know this distinction, and hearing you cite it moves you from “knows the syntax” to “knows the practice.”


Return to README.md · Next: projects.md