Part 0 - Introduction

Memory

It is possible for us to say that, in order to have a good solid grasp of any systems programming language, I should be able to tell what is happening beneath the ram. And that is why there is a dedicated portion for this section of learing C - learning memory

#c #memory

Before Hello World

The Machine Has No Mercy

The first computers had no languages. No syntax. No abstractions.

You programmed them by physically rewiring them. ENIAC, 1945 — to change the program, you moved cables. The machine didn’t care what you meant. It did exactly what the wires said. Nothing more.

This is the first truth about computers:

The machine is not on your side. It executes. It does not interpret.

Every language ever built is a human attempt to bridge that gap — to let humans think like humans while the machine executes like a machine. And every layer of abstraction is simultaneously a gift and a blindfold.

Stuxnet’s authors understood what was under the blindfold. That’s why it worked.

Assembly — Speaking to the CPU Directly

By the late 1940s, people realized: the rewiring was insane. So they invented assembly language.

Instead of moving cables, you wrote:

MOV AX, 1
ADD AX, 2

A program called an assembler translated those mnemonics directly to the bytes the CPU understood. One instruction = one machine operation. One-to-one. No magic.

This was revolutionary. But it was also completely tied to the hardware. Code written for one CPU ran on nothing else. And writing anything complex — an operating system, a compiler — was an act of insane manual labor.

But here’s what assembly gave you that nothing since has equaled:

Total visibility. You saw every byte. You controlled every register. The machine had no secrets.

This is why exploit developers still think in assembly. Not because it’s pleasant. Because that’s the level reality actually operates at.

FORTRAN — The First Rebellion (1957)

John Backus at IBM looked at his team spending 80% of their time on bookkeeping — tracking registers, managing memory manually — and said: this is insane.

He built FORTRAN. Formula Translation. Aimed at scientists and engineers doing math.

For the first time: write X = Y + Z and the compiler figures out the registers. Nobody believed it would work. The skeptics said the generated code would be too slow, too bloated.

Backus made it work. The generated code was nearly as fast as handwritten assembly.

This was the proof of concept for the entire idea of high-level languages.

But FORTRAN was narrow. Science and math. Numbers. Not systems. Not operating systems. Not hardware control.

COBOL, LISP — 1958–1960

Two languages, two completely different philosophies born almost simultaneously.

COBOL — for business. English-like syntax. Grace Hopper’s fingerprints all over it. The idea that non-mathematicians could program. It was verbose, readable, and it ran the world’s financial systems. Still does. There is more COBOL running today than most people alive know.

LISP — John McCarthy. For artificial intelligence. Based on lambda calculus. Everything is a list. Code is data. Data is code. No distinction.

LISP introduced ideas that took 40 years for other languages to catch up to: garbage collection, dynamic typing, first-class functions, macros that rewrite the language itself.

These two couldn’t be more different. COBOL said: the machine serves the human. LISP said: thought itself has a structure and code should mirror it.

The 1960s — The Complexity Crisis

By the 1960s, software was eating itself.

Programs were getting large. Bugs were getting catastrophic. The NATO Software Engineering Conference of 1968 officially named it: the software crisis. Projects late, over budget, broken. Some never delivered.

The problem: humans think in structure. The machines executed in chaos. GOTO statements everywhere. Programs that were impossible to reason about.

Edsger Dijkstra wrote his famous letter: “Go To Statement Considered Harmful.”

The answer the field converged on: structured programming. Functions. Loops. Conditionals. Block scope. The idea that code should have a shape that matches human reasoning.

BCPL → B → C (1967–1972)

Here’s where your story begins.

Martin Richards at Cambridge wrote BCPL in 1967. A simple, typeless language for writing systems software. Stripped down. No types — everything was a machine word.

Ken Thompson at Bell Labs took BCPL, simplified it further, and called it B. Used it to write an early version of Unix.

But B had a problem. It was typeless. You couldn’t efficiently handle characters vs integers vs pointers on the new PDP-11 hardware, which had byte addressing.

Dennis Ritchie took B and added a type system. Structs. Proper pointer arithmetic. The ability to write to arbitrary memory addresses cleanly.

He called it C.

In 1972, Unix was rewritten in C. This was the moment.

An operating system — something that was always written in assembly, because everyone knew that was the only way to get the performance — was written in a high-level language. And it was fast. And it was portable.

For the first time: write the OS once, port it to new hardware by rewriting a small assembly core. The rest comes free.

C didn’t win because it was the most elegant language. It won because it was the thinnest possible layer over the machine that was still a language. It gave you types, functions, and structure — and took almost nothing in return. No garbage collector. No runtime. No safety net.

The abstraction cost was nearly zero.

The Unix Philosophy — 1970s

C and Unix co-evolved. Each shaped the other.

The philosophy that emerged:

  • Write programs that do one thing well
  • Write programs that work together
  • Write programs that handle text streams

Small, composable tools. Pipelines. The shell as a language for connecting them.

This is still the skeleton of every Linux system you run today. The terminal you used to build your Wayfire shell — that lineage goes directly back to Thompson and Ritchie at Bell Labs, 1969.

The Language Wars — 1980s

C spread everywhere. But people started wanting more.

C++ — Bjarne Stroustrup, 1983. C with classes. The attempt to add object-oriented programming without sacrificing C’s performance. It succeeded, but at the cost of becoming enormously complex. C++ became a language where experts disagree about what is safe to use.

Ada — designed by the US Department of Defense. Safety-critical systems. Extremely strict typing. Designed so that if it compiled, it probably did what you meant. Used in aircraft, missiles, space systems to this day.

Pascal — Niklaus Wirth. Clean, structured, designed for teaching. Died in practice because it was too restrictive for systems work.

Smalltalk — pure object-oriented. Everything is an object. Everything. Integers are objects. Classes are objects. It was conceptually beautiful and influenced every OO language that came after.

The 1980s also brought the microcomputer revolution. Suddenly C wasn’t just for Unix workstations. It was for the IBM PC. The Apple II. The machines on every desk. C became the lingua franca of an exploding industry.

The 1990s — The Internet and Managed Languages

The internet changed everything.

Suddenly software ran on machines you didn’t control. Users you couldn’t trust. Networks that were hostile.

C’s lack of safety — the very thing that made it powerful — became a liability at scale. Buffer overflows. Use-after-free bugs. Format string vulnerabilities. All of these became weapons. The internet taught us that every C bug was a potential door into the machine.

Java — 1995. Sun Microsystems. The answer to C’s portability and safety problems. Write once, run anywhere. Garbage collected. No pointers. Type safe. It ran in a virtual machine — the JVM — which was itself written in C.

Java was slower than C. Programmers complained. Sun kept making the JVM faster. Eventually Java was fast enough for almost everything. Enterprise software migrated to it en masse.

Python — also 1991. Guido van Rossum. Clean syntax. Interpreted. Even slower than Java, but supremely writable. Scientists, data analysts, researchers adopted it. The idea: programmer time is more expensive than CPU time. Make the human fast.

JavaScript — 1995. Brendan Eich wrote it in 10 days. It was supposed to be a small scripting language for web pages. It became the most deployed language in human history. Every browser runs it. Its design has flaws Eich himself has apologized for. It runs the world anyway.

The 2000s — Complexity Eating Itself Again

The software crisis never went away. It just changed shape.

Programs got larger. Teams got larger. Languages accumulated features. C++ became so complex that compilers disagreed on what valid C++ was.

Security became existential. Every buffer overflow was an exploit. Every null dereference was a crash. Every use-after-free was a potential Stuxnet.

The industry tried to solve safety with tooling — static analyzers, sanitizers, fuzzing. They helped. They didn’t solve it.

The fundamental problem: C trusts the programmer completely. And programmers make mistakes. At the scale of millions of lines of code, across hundreds of developers, over decades — the bugs were guaranteed.

Rust — 2010 (Mozilla), 2015 (stable)

Someone asked: what if you could have C’s performance and control and have the compiler guarantee memory safety?

The answer was Rust.

Graydon Hoare started it. Mozilla funded it to rewrite Firefox’s rendering engine.

The core idea: ownership. Every piece of memory has exactly one owner. When the owner goes out of scope, the memory is freed. Automatically. Without a garbage collector. At compile time.

No use-after-free — the compiler won’t let you. No double-free. No data races — the ownership system prevents them structurally.

If it compiles, a huge class of bugs is impossible.

The cost: the compiler is strict. Sometimes brutal. The borrow checker fights you. Expressing certain patterns requires understanding ownership deeply.

You already know this. You’ve written Rust. You’ve felt the borrow checker push back.

Here’s the thing about your C journey:

Rust taught you what the rules are. C is what happens when there are no rules.

You already have the mental model of ownership. C will show you what it looks like when you have to enforce it yourself, manually, in your head, every line, with nothing checking your work.

That’s not a disadvantage. That’s the sharpest possible entry point into C.

Where We Are Now

Every major language exists on a spectrum between:

Control ←————————————————————→ Safety
  C / Assembly              Rust / Haskell / Ada
                    C++
              Java / Go
         Python / JavaScript

C sits at the control end. Not because nobody thought of safety — because the designers made a deliberate choice. Trust the programmer. Get out of the way. Let them reach the metal.

That choice is why C is still everywhere 50 years later:

  • The Linux kernel
  • Every OS kernel
  • Every embedded system
  • Network firmware
  • Compilers themselves
  • Python’s runtime
  • The JVM
  • SQLite
  • OpenSSL

And it’s why Stuxnet was written at the level it was. The authors didn’t fight the machine. They understood it completely — memory, registers, interrupts, PLC instruction sets — and bent it to their purpose.

What Chapter 0 Leaves You With

Programming languages are not the point.

They are human-readable descriptions of machine state transformations.

The machine doesn’t know C. It knows voltages, electrons, clock cycles. Every language is a translation layer. C is the thinnest layer humans have found practical. Assembly is no layer at all.