Video summary
How I program C
Main summary
Key takeaways
Summary (Advanced C programming: programming style, tools, and low-level concepts)
The speaker argues for an “advanced but controlled” approach to C programming: embrace C’s explicitness and complexity (rather than avoiding it), build long-lasting systems, and rely on compiler help, strict clarity, and custom tooling for debugging and documentation. The talk is presented as a high-level guide to how the speaker programs, with examples spanning memory management, API design, naming conventions, and performance/correctness tradeoffs.
Core philosophy & approach
- Results → control: Start simple, but eventually you want control over complexity. Developers often begin by seeking convenience (e.g., simple HTML), then later realize deeper mechanisms (tags/CSS/features) are necessary.
- Learn “how it’s made”: Programming well requires wanting to understand the underlying complexity—like asking what tools/ingredients a kitchen provides, not just ordering a finished omelet.
- C’s longevity: C persists because many newer language designs carry assumptions that C avoids; C’s “problem” isn’t losing, it’s being optimized for different priorities—control and explicitness.
Memory management: control beats convenience
Garbage collection vs manual memory control
- The speaker critiques garbage collection:
- It reduces manual work early.
- Later it can harm performance and remove timing/control (e.g., freeing memory when it’s optimal).
- Manual memory problems are framed as solvable with the right tooling and programmer control.
Custom memory debugging with macros
- Uses
__FILE__/__LINE__(via macros) to build a memory tracker:- Wrap allocation/free to record where memory was allocated and freed.
- Print allocations and detect memory leaks.
- Adds guard/magic techniques to detect overwrites:
- Over-allocates and inserts sentinel values after allocations.
- Verifies integrity to catch buffer overruns.
- Treats debugging as a strategy to make bugs visible:
- Crashes are useful because they force the fix.
OS/virtual memory and safety boundaries
- Explains virtual memory and memory page permissions (read/write/execute).
- Highlights that memory bugs can be caught when code writes outside its authorized region.
realloc as “not necessarily slow”
- Argues
reallocis often better than feared:- Memory may not be contiguous at the hardware level.
- The OS/hardware can remap pages/blocks.
- Contrasts common advice (“
reallocis bad because it copies”) with the speaker’s view that virtualization can reduce practical copying cost.
Cache behavior & performance
- Portrays memory access as much slower than arithmetic:
- Registers/L1 cache are fast.
- Main memory access can be ~50 cycles versus compute that can run far more frequently.
- Optimization focus:
- reduce memory accesses
- improve locality so data stays in cache
- use arrays instead of pointer-chasing structures
Data structures & performance patterns
Arrays vs linked lists
- Argues linked lists are often poor for memory coherence:
- Nodes are scattered in memory → more cache misses.
- Arrays provide adjacency → better cache utilization.
Dynamic arrays and realloc strategy
- Recommends growth strategies for resizable arrays:
- allocate in chunks (e.g., grow by factor/step)
- keep
reallocrelatively rare
- Supports fast removal by swap with end (order not preserved) rather than shifting/moving elements.
“Clarity over cleverness” in code + compiler errors as a feature
- Rejects the idea that “two lines of code” is inherently good:
- prefers code that is readable and correct.
- Claims ambiguity is the enemy:
- code should be unambiguous to humans and compilers
- prefer compiler errors over silent guesses
- Criticizes “clever” features (notably in C++):
- operator overloading can obscure intent (e.g., whether
vector * vectormeans dot product or elementwise multiply) - overload resolution with numeric literals can silently pick the wrong implementation (int/float/double)
- operator overloading can obscure intent (e.g., whether
- Reinforces explicitness with patterns like type-suffixed literals (e.g.,
4.0f) to avoid unintended overloads.
Tooling & documentation generation
- Builds a personal website/docs generator by parsing C source:
- extracts comments
- associates them with functions
- generates browsable documentation directly from the codebase
- Also mentions writing custom tools, including a debugger-like tool.
Naming conventions & coding style (as correctness aids)
- Prefers descriptive, consistent, long-ish names (“wide code”).
- Uses naming patterns to reduce confusion:
- types vs functions vs variables formatted differently so roles are visually distinct
- strict spacing rules to improve search accuracy (avoids ambiguous matches in text search)
- Uses semantic naming examples for conventions:
count,length,found,next,previous- consistent prefixes/suffixes:
Funksuffix for function pointersinternalfor internal-only functions
Function size & structure: “long sequential is readable”
- Contrasts with “max 5 lines” CS advice.
- Advocates long sequential functions when the logic is naturally linear (e.g., a render loop):
- easier to follow state changes over time
- reduces hidden control flow complexity
API and modular design in C
External interfaces vs internal headers
- Uses a module style:
- one external
module.hpresents the API - internal details kept in
internal.h(or similarly marked files)
- one external
- Favors “directory-like” naming:
- function names reflect module ownership (e.g.,
ImagineLibrary_*,ImagineMutex_*) to locate code quickly.
- function names reflect module ownership (e.g.,
API design pattern: define the interface first
- Builds the public API surface first, then fills internal implementations.
- Makes it easier to swap internal implementations without changing the external interface.
Object-oriented patterns without “C objects”
- Argues that true OO (“code + data together”) can be misleading in C:
- code and data are separate (and tied to security: code pages are non-writable).
- Implements OO-like behavior using:
- handles returned from
*_create()(often asvoid*) - functions that operate on the handle (e.g.,
*_send(handle, ...))
- handles returned from
- Uses
void*as opaque/encapsulated pointers:- external code can’t inspect internal representation
- internal structures can change without breaking callers
- Shows how a library can define internal structs while exposing only
void*handles.
Macros: “useful for rare power, but avoid cleverness”
- Generally dislikes macros, but approves specific uses:
- generic code for multiple types (where repetition is unavoidable)
- debugging instrumentation using file/line
- Demonstrates macro-based “verbose errors” for binary packing/unpacking:
- when unpacking fails, errors can mention expected type/name and include file/line
- targets bugs common in binary protocols where offset/field mismatches cause silent corruption
Memory/pointer fundamentals (conceptual explanations)
- Explains pointers as addresses into a conceptual “byte street,” where types determine stepping size.
- Notes that arrays are essentially pointer arithmetic.
- Discusses
structlayout:- structs compile to field offsets + total size
- alignment/padding can increase struct size beyond the sum of fields
- field ordering can reduce wasted padding (though arrays still follow alignment rules)
- Mentions “dangerous but possible” low-level packing tricks (manual unaligned reads/writes) with associated risk.
UI/application architecture in C (practical design)
- Describes a UI widget model where widgets either:
- draw, or
- process events/updates
- Critiques an OO/event system requiring managing many IDs for moving/drawing widgets.
- Proposed approach:
- use pointers (unique identities) as widget IDs
- store widget state internally
- invoke a widget for different reasons via the same function interface
- Emphasizes collision/overdraw correctness:
- UI layering is tracked so input targeting respects which widget is actually visible.
Ending tools/resources and topics mentioned
- Recommends “classic” algorithms/constants:
- fast inverse square root approximation (
Q_rsqrtstyle) - a simple seeded random number generator
- fast inverse square root approximation (
- Provides resources/locations:
- website where code/libraries and projects are published
- open-source code (FreeBSD license mentioned)
- asks for bug reports and provides contact/social handles
Main speakers / sources
- Speaker: the single presenter (no named co-speakers). References to their own website/projects and open-source libraries appear throughout.
- External references mentioned (as topics/links, not quoted sources):
- Linux/Windows memory tooling (e.g., “G Flags”)
- C/C++ behavior (C++ operator overloading and overload resolution)
- OpenGL state behavior
- fast inverse square root and seeded RNG concepts (described as known techniques)
- John Carmack, via a tweet about fly-by-wire code reliability