Edited by humans. Written by AI. How our editing works
All articles

Building Huffman Trees in C, Live and Unfiltered

Dr. Jonas Birch codes a Huffman tree in C from scratch, unions, seg faults and all. Here's what that pedagogy reveals about systems programming culture.

Dev Kapoor

Written by AI. Dev Kapoor

August 30, 20267 min read
Share:
A stylized tree icon on torn paper against a digital matrix background with text reading "huffman tree in C

Photo: AI. Júlia Almeida

Dr. Jonas Birch sits somewhere interesting in the systems programming tutorial ecosystem. His channel isn't chasing the algorithm-for-interviews crowd, and he's not producing the kind of polished, sponsor-laden content that dominates YouTube CS education. His ongoing series on coding your own operating system in C and assembly has built a following among people who think the right way to understand a computer is to argue with it directly, at the lowest level that doesn't involve soldering. His latest episode, coding a Huffman tree in C, lands squarely in that tradition: unscripted, genuinely exploratory, and not particularly interested in making things look easy.

That context matters for why this tutorial is worth your attention. Birch isn't teaching Huffman coding to people who need a leetcode refresher. He's building toward a compressor, probably for use in his OS project, which means the design decisions he makes here carry actual downstream weight. When he chooses a union to represent both leaves and nodes in a single tree type, he's not illustrating a textbook concept. He's solving a real structural problem: a sorted list of tree elements needs to hold both character leaves and internal nodes interchangeably, without the overhead of a vtable or the abstraction cost of a tagged void pointer scheme. That's a choice with consequences, and he makes it on camera, narrating the reasoning as he goes.

What Huffman actually does, and why C is a loaded choice for teaching it

The algorithm itself is about a century old, conceptually. As Virginia Tech's CS3 materials describe it, a Huffman code derives from a full binary tree built on character frequencies: the more often a character appears, the shorter its bit representation. Programiz summarizes the construction as building the tree first from frequency data, then deriving codes from the tree's structure. Birch opens with exactly that sequence: count characters, sort by frequency, pair the lowest-frequency elements, build upward. Four steps. Done. The algorithm isn't the hard part.

The hard part, in C, is the data structure design that makes the algorithm actually work. And this is where Birch's choice of language becomes a live argument, not just a stylistic preference.

The systems programming community is currently having a specific fight about C. The short version: Rust exists, memory safety is provably achievable, and the number of critical CVEs traced to C's manual memory management is not a philosophical concern but a documented body count. The Linux kernel's gradual, contested Rust adoption is one front of this fight. Mozilla's decision to rewrite core browser components in Rust is another. Against that backdrop, a tutorial that builds a tree data structure in C using malloc, pointer arithmetic, and unions is not a neutral pedagogical choice. It's a position.

I don't think Birch is making a political statement by choosing C. He's building an OS, and C is still the language in which most operating systems exist. The Huffman tree is a component of a larger project, and switching languages mid-project for a compressor module would be its own kind of chaos. But the choice does mean the tutorial carries the full weight of C's manual memory model, and the seg fault that arrives during testing makes that point more vividly than any slide deck could.

The union problem, and what it teaches

Birch's core data structure is a union tree that can be either a struct leaf (holding a character, a frequency counter, and an upstream connector) or a struct node (holding left and right downstream connectors, an upstream connector, and a frequency). The union lets a sorted list treat both types as the same pointer-sized thing, which is exactly what you need when you're building the tree bottom-up from a frequency-sorted list.

"We need to have a solid data type that can handle all of this," he says, "and that's where we are going to begin."

The frequency field is typed as int64 because, as Birch reasons aloud, the input might be a large file. The character field is int8. The upstream and downstream connectors are tree pointers. Straightforward on paper.

The problem arrives when he starts writing the node constructor and tries to access frequency fields on the union's members using arrow notation, treating the embedded structs as pointers when they're actually embedded values. The seg fault that follows is not a beginner's mistake exactly; it's the kind of thing that happens when you're designing a data structure by thinking through it in real time rather than speccing it on a whiteboard first. The union's memory layout makes pointer-to-member assumptions dangerous in ways that aren't immediately obvious.

"These unions are not always so straightforward," Birch says, after several minutes of debugging, just before the fix clicks into place.

The fix is a search-and-replace from arrow-notation to dot-notation through the affected struct members, acknowledging that the embedded structs in the union are values, not pointers. It works. The tree prints correctly: a node with frequency five, connected left to a leaf with frequency four (character 'A') and right to a leaf with frequency one (character 'B'), with upstream and downstream connections all intact.

What the debugging sequence actually demonstrates

The seg fault sequence is where this tutorial earns its runtime, and where Birch's approach diverges most sharply from the edited, mistake-free pedagogy that dominates the space.

Watching him work through it is instructive in a way that a clean solution wouldn't be. He uses return-statement bisection to isolate the crash site. He runs ltrace and acknowledges it didn't help. He considers multiple hypotheses, eliminates them one by one, and arrives at the correct diagnosis through the kind of incremental, unglamorous reasoning that actual debugging requires. None of this is scripted. At one point he says, "I don't see how this could seg fault, but it can. It most definitely can."

That's the sentence that should go on a poster in every CS department that teaches C. Manual memory management doesn't just fail at the obvious moments. It fails at the moments you were confident about.

Programming Logic's implementation guide for Huffman coding in C covers the frequency-analysis and tree-construction phases at a higher level, with cleaner code. It's a useful reference. But it doesn't show you what the union member access confusion looks like when it happens, or how you find it. Birch does.

This is the pedagogical bet he's making: that seeing the mistake and the recovery teaches something that seeing only the solution cannot. I think he's right about that, and I think it's a bet that scales better to the actual experience of writing systems code than the alternative. Nobody ships C without debugging it. The question is whether your first encounter with union memory layout confusion happens in a tutorial or in production.

The Rust question this tutorial sidesteps

Here's the thing Birch doesn't address, and that I think is worth naming directly: the problem he spent significant debugging time on, pointer arithmetic confusion in a union-based tagged type, is a category of bug that Rust's type system catches at compile time. The enum in Rust that would represent this union tree would give you pattern matching, exhaustiveness checking, and the guarantee that you cannot dereference a node as a leaf without the compiler noticing. The seg fault simply doesn't exist in that version of this code.

That's not an argument that Birch should be teaching this in Rust. His project context makes C the right tool. But it does locate this tutorial in a real argument the community is having: when we teach systems programming fundamentals in C, we're also teaching people to navigate a class of errors that newer systems languages have largely eliminated. The debugging practice is real. The lesson about union semantics is real. And so is the question of whether, for a learner starting fresh today with no existing OS project to maintain, C is still the first-principles language worth that cost.

Birch closes the episode noting that the hard structural work is done, and that sorting and text parsing come next. Whether the series eventually produces a working compressor for his OS project is an open question. But the union is connected, the tree prints, and the seg fault is gone.

For now, that's enough.


By Dev Kapoor, Open Source and Developer Communities Correspondent, Buzzrag

More Like This

Green owl mascot with large eyes on a matrix-code background promoting a C programming advanced concepts tutorial

What Actually Happens When You Run printf() in C

Dr. Jonas Birch's tutorial reveals the three-layer journey from C library calls to system calls to CPU instructions—using printf() as the unlikely hero.

Yuki Okonkwo·5 months ago·5 min read
Multiple file types (.txt, .txg, .png, .mp4) compress into a ZIP folder with a glowing arrow, highlighting 70% size…

Three Hours of Debugging a File Compressor in C

Dr. Jonas Birch spent 3.5 hours live-coding a file compressor in C. What the session reveals about real programming work might surprise you.

Marcus Chen-Ramirez·5 months ago·6 min read
A cartoon penguin studies math equations at a desk with books and calculator, surrounded by algebraic formulas on a…

Squaring in C: Loops, Bit Shifts, and Readability

Jonas Birch's C tutorial on writing and optimizing a squaring function surfaces a debate C communities never really resolved: readable code vs. raw performance.

Dev Kapoor·1 week ago·1 min read
Puzzle pieces showing assembly code structure with if/then/else conditional logic and ENDIF statement against dark background

Nested If-Else in NASM Assembler Macros Explained

Dr. Jonas Birch shows how NASM's context stack enables nested if-else constructs in assembly macros—and what that reveals about systems programming culture.

Dev Kapoor·1 month ago·7 min read
A blue and white mascot character stands against a digital matrix background with large white text reading "8 HOURS OF C…

Building Tor Network Tools and Encryption in C

Dr. Jonas Birch's 8-hour C programming series covers building Tor proxy tools, RC4-based encryption, and Linux file system security from scratch.

Rachel "Rach" Kovacs·3 weeks ago·7 min read
A large blue C logo wrapped by a green snake with code editor window, network nodes, and programming icons on a dark blue…

Building a Reinforcement Learning Library in C from Scratch

Harsh Bhatt's freeCodeCamp course builds a full RL library in C—autograd engine, Snake environment, and REINFORCE—without any ML frameworks.

Yuki Okonkwo·1 week ago·7 min read
Man wearing glasses in profile against black background with white and green text identifying speakers Don Lincoln and Lex…

Physics' Unfinished Project: The Standard Model's Open Issues

Don Lincoln on the Standard Model, string theory, dark matter, and why physics' biggest project has known bugs no one can fix—yet.

Dev Kapoor·3 months ago·9 min read
Man with glasses looking shocked at anime character profile marked "Busy" with red "REVENGE" text overlaid

Yellow Key: The BitLocker Bypass Microsoft Didn't Want Public

A researcher dropped six Microsoft zero-days and got banned from GitHub and GitLab. Here's what the Yellow Key BitLocker exploit actually does—and what it reveals.

Dev Kapoor·3 months ago·7 min read

RAG·vector embedding

2026-08-30
1,847 tokens1536-dimmodel text-embedding-3-small

This article is indexed as a 1536-dimensional vector for semantic retrieval. Crawlers that parse structured data can use the embedded payload below.