Skip to main content

Module 1: C Programming Fundamentals: Case Studies

These case studies make C's basic model concrete: translation units, headers, strings, stdio, integer conversions, and undefined behavior.


Case Study 1: Header Definition Breaks The Linker

Scenario: A learner defines int debug = 1; in a header included by three .c files. Compilation works; linking fails with multiple definitions.

Source anchor: C's compile/link model is grounded in translation units and linkage; use this with gcc -E/-S/-c and linker errors as evidence.

Module concepts: declaration, definition, header, translation unit, linkage.

Wrong Approach

Put variable definitions in headers.

Better Approach

Use extern declaration in header and one definition in a .c file:

/* config.h */
extern int debug;

/* config.c */
int debug = 1;

Tradeoff Table

ChoiceGainCost
Define in headerLooks convenient in one fileBreaks at link time across translation units
extern + one definitionCorrect linkage modelRequires a dedicated definition site
static in headerAvoids linker clashCreates separate copies per translation unit

Failure Mode

The build passes compilation on every .c file, then fails at link time with multiple-definition errors that are hard to trace back to the header.

Required Artifact

Build a three-file example and annotate preprocessor, compiler, assembler, linker outputs.

Project / Capstone Connection

Use this pattern for shared configuration, logging flags, and global state in C utilities built later in the semester.


Case Study 2: Unsafe String Copy

Scenario: A CLI copies argv input into char name[32] with strcpy. Long input overwrites adjacent stack data.

Source anchor: SEI's C security discussion uses buffer overflow examples to show why copying larger strings into smaller arrays is dangerous. See SEI: Improving Security in the Latest C Standard.

Module concepts: C string, null terminator, buffer size, bounds.

Wrong Approach

"The input is probably short."

Better Approach

Track buffer capacity explicitly:

char name[32];
snprintf(name, sizeof name, "%s", argv[1]);

Tradeoff Table

ChoiceGainCost
strcpy into fixed bufferMinimal codeNo bounds protection
snprintf with sizeofEnforces capacity and terminator handlingPossible truncation to account for
Dynamic allocation from input lengthFits arbitrary inputMore memory-management complexity

Failure Mode

An unusually long argument overwrites adjacent stack state, causing crashes, silent corruption, or exploitable behavior far from the copy site.

Required Artifact

Write a string-safety checklist for input, copy, formatting, and termination.

Project / Capstone Connection

Apply this checklist to every CLI parser, config loader, and text-processing tool you build in C.


Case Study 3: scanf Leaves A Broken Input Stream

Scenario: A menu program uses scanf("%d", &choice). When the user types abc, the loop repeats forever.

Source anchor: C stdio behavior is observable through standard library docs; this case reinforces why fgets plus parsing is safer for line-oriented input.

Module concepts: stdio, formatted input, error handling, input buffer.

Wrong Approach

Assume scanf always consumes invalid input.

Better Approach

Read a full line and parse:

char line[64];
if (fgets(line, sizeof line, stdin)) {
char *end = NULL;
long value = strtol(line, &end, 10);
}

Tradeoff Table

ChoiceGainCost
Direct scanf loopShort code for happy pathFragile recovery on invalid input
fgets + strtolClear error handling and buffer controlMore parsing code
Custom tokenizer layerReusable input contractMore upfront abstraction

Failure Mode

Invalid characters remain unread, so the loop repeatedly sees the same bad input and appears hung to the user.

Required Artifact

Write a robust menu input loop with invalid-input recovery.

Project / Capstone Connection

Use this line-oriented input pattern for shell-like tools and interactive debuggers later in the systems track.


Case Study 4: Integer Conversion Surprise

Scenario: A function compares signed int len = -1 with unsigned size_t capacity = 16. The comparison behaves unexpectedly.

Source anchor: C integer conversions are a core language rule; use compiler warnings and -Wall -Wextra -Wconversion as evidence.

Module concepts: signed/unsigned conversion, integer rank, warnings.

Wrong Approach

"The comparison reads like math."

Better Approach

Use compatible types and validate boundaries:

if (len < 0) return ERROR;
if ((size_t)len > capacity) return TOO_LARGE;

Tradeoff Table

ChoiceGainCost
Mixed signed/unsigned compareNo extra codeEasy to misread and miscompile mentally
Validate sign before castMakes intent explicitRequires disciplined boundary checks
Standardize type choices earlierFewer conversions overallCan require wider API cleanup

Failure Mode

A negative sentinel converts to a huge unsigned value, so range checks or loop bounds pass when they should fail.

Required Artifact

Create a conversion table for five signed/unsigned expressions and compiler warnings.

Project / Capstone Connection

Carry this into buffer-length, file-size, and syscall-wrapper code where signed error returns meet unsigned sizes.


Case Study 5: Macro With Double Evaluation

Scenario: #define MAX(a,b) ((a) > (b) ? (a) : (b)) is called as MAX(i++, j++).

Source anchor: The C preprocessor performs textual substitution, not function-call evaluation.

Module concepts: macro expansion, side effects, inline function.

Wrong Approach

Treat macros like functions.

Better Approach

Avoid side-effect arguments or use functions where possible:

static inline int max_int(int a, int b) {
return a > b ? a : b;
}

Tradeoff Table

ChoiceGainCost
Text macroGeneric and zero call syntax overheadSide effects and type surprises
static inline functionSingle evaluation and debuggable behaviorType-specific unless wrapped deliberately
Precompute arguments before macroKeeps existing macroDepends on caller discipline

Failure Mode

One operand is evaluated twice, so counters advance unexpectedly and behavior changes when debugging or optimizing.

Required Artifact

Run gcc -E and show the expanded macro that evaluates an argument twice.

Project / Capstone Connection

Use this case when deciding whether helper logic in later C projects belongs in macros, inline functions, or normal functions.


Source Map

SourceUse it for
SEI C security discussionbuffer overflow motivation
GCC warning optionscompiler diagnostics
C standard drafts / cppreference Clanguage and library reference

Completion Standard

  • At least three artifacts are completed.
  • At least one artifact shows compile/link stages.
  • At least one artifact uses compiler warnings to catch a bug.