Capstone — Building a Small Project

Course 3 · Ch 6 — Track Finale
Capstone: Building a Small Project
A persistent key-value store, combining nearly every chapter of this entire 21-chapter track

Twenty chapters, one piece at a time. This closing chapter builds kvstore — a real, persisted, command-line key-value store — combining structs, a hand-built hash table, dynamic memory, file I/O, and a genuine multi-file Makefile build into one working program.

The Project — A Persistent Key-Value Store

$ kvstore set name Alice $ kvstore set role admin $ kvstore get name Alice $ kvstore delete role $ kvstore get role (not found)

Every value survives between runs — loaded from disk on startup, saved back after any change.

The Data Structure

c3-2's hash table, reused directly — buckets of linked key-value pairs, separate chaining for collisions.

typedef struct Entry { char *key; char *value; struct Entry *next; } Entry; typedef struct { Entry *buckets[64]; } HashTable;

Reading & Writing Persistence

c2-6's File I/O — a simple text format, one key=value per line, loaded on startup and rewritten after every mutation.

void save_to_file(HashTable *table, const char *path) { FILE *fp = fopen(path, "w"); if (fp == NULL) return; for (int i = 0; i < 64; i++) { for (Entry *e = table->buckets[i]; e != NULL; e = e->next) { fprintf(fp, "%s=%s\n", e->key, e->value); } } fclose(fp); }

The CLI Interface

c1-5's functions and c1-8's string handling, applied to argv.

int main(int argc, char *argv[]) { if (argc >= 2 && strcmp(argv[1], "set") == 0 && argc == 4) { ht_set(table, argv[2], argv[3]); save_to_file(table, "kvstore.db"); } // "get" and "delete" follow the same pattern }

Memory Management Throughout

Every key and value is duplicated onto the heap when inserted — c2-2's discipline, applied consistently end to end: freed on delete, and freed entirely on program exit, matching c3-2's own free_list-style traversal, now applied per bucket.

Structuring the Project With a Makefile

Split into hashtable.c/.h, storage.c/.h, and main.cc2-5's real multi-file discipline, with a Makefile tying it all together.

kvstore: main.o hashtable.o storage.o gcc main.o hashtable.o storage.o -o kvstore main.o: main.c hashtable.h storage.h gcc -c main.c -o main.o hashtable.o: hashtable.c hashtable.h gcc -c hashtable.c -o hashtable.o storage.o: storage.c storage.h hashtable.h gcc -c storage.c -o storage.o

Where Each Piece Came From

PieceChapter
Hash table + separate chainingc3-2
File save/load formatc2-6
CLI argument parsing, string comparisonc1-5, c1-8
malloc/free discipline for every key/valuec2-2
Multi-file build, Makefilec2-5
Verified with valgrind/ASan during developmentc2-3, c3-5

What's Still Out of Scope, Honestly

kvstore is not thread-safe — no locking (c3-3) protects concurrent access to the file or the hash table. Values are plain strings only, not arbitrary binary data. There's no recovery logic for a corrupted save file. And no automated test suite or CI pipeline is wired up in this chapter itself, even though valgrind/ASan were used manually during development — a real next step, not something this capstone claims to have solved.

The throughline, restated
Every piece of this capstone is a direct, unmodified application of a chapter's own material — nothing new was introduced here. That's deliberate, matching the same pattern this site's other capstones follow: real capability comes from combining well-understood, individually simple pieces correctly.
This is still C — every discipline from this track still applies
Nothing about writing a "real" project changes any of the rules covered since Chapter 1. Every allocation still needs its matching free, every array access is still unchecked, every pointer can still dangle. Scale doesn't relax the discipline — it just multiplies the number of places it has to be applied correctly.

Closing the Course & Track

From c1-1's compile-then-link model to a real, persisted, multi-file program — every chapter in between added one specific piece of manual control C leaves entirely to the programmer, and one specific comparison to what Rust's compiler enforces instead. Twenty-one chapters, one throughline: C trusts you completely. Understanding exactly what that trust costs, and exactly what a compiler-enforced alternative buys back, is the actual lesson this entire track was built to teach.

Coding Challenges

Challenge 1

Identify which specific chapter each of the following pieces of kvstore came from: (a) the separate-chaining collision strategy, (b) the save-file format, (c) the discipline of freeing every key and value on program exit.

📄 View solution
Challenge 2

Explain why kvstore, as described in this chapter, is not safe if two instances of it are run concurrently against the same kvstore.db file — name the specific chapter's material that would need to be applied to fix this.

📄 View solution
Challenge 3

Across the entire 21-chapter C track, name the single idea that recurs most often, and explain in your own words why understanding it deeply matters more than memorizing any individual function or syntax rule.

📄 View solution

Chapter 6 Quick Reference — Course & Track Complete

  • kvstore combines: hash table (c3-2), file persistence (c2-6), CLI parsing (c1-5/c1-8), malloc/free discipline (c2-2), and a real Makefile build (c2-5)
  • Still out of scope, honestly: thread safety, binary values, corrupted-file recovery, automated testing
  • Every discipline from Chapter 1 onward still applies at capstone scale — nothing gets relaxed
  • Course 3 complete — C Advanced, 6 chapters
  • Full C track complete — Fundamentals + Intermediate + Advanced, 21 chapters across 3 courses