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.