Dataslope logoDataslope

Why C for Systems Programming?

Why C is still the dominant language for operating systems, runtimes, databases, and embedded software

C was designed in 1972 by Dennis Ritchie at Bell Labs as a portable language for rewriting the Unix kernel. More than fifty years later, it is still the language that the world's most important systems are written in. Knowing why will help you understand what to expect from the language — and what not to expect.

C runs the world

SoftwareWritten in
Linux, BSD, XNU (macOS), Windows NT kernelsC (with bits of C++ / assembly)
Python's reference interpreter (CPython)C
Node.js (V8, libuv)C / C++
SQLite, Redis, PostgreSQLC
nginx, OpenSSH, OpenSSL, curl, FFmpeg, GitC
Most embedded firmware (microcontrollers, routers, cars)C

When you pip install a wheel, apt-get a package, or open a TLS connection, you are almost certainly running C code that someone compiled years ago.

A small language, close to the machine

The C language itself is tiny. The whole grammar fits in a thin book. There are no classes, no exceptions, no garbage collector, no generics, no built-in dictionaries, no string type — just integers, floats, pointers, arrays, structs, functions, and a preprocessor for text substitution.

That smallness is a feature. It means a compiler is small enough to exist for any chip you can name, and the runtime overhead of a C program can be measured in kilobytes.

Code Block
C 17 (201710L)

That program, once compiled for a real CPU, is a few kilobytes of machine code and a handful of printf calls in libc. There is no virtual machine. There is no interpreter. The operating system loads it and jumps to main.

"Portable assembly"

People sometimes call C portable assembly because the language exposes the same model of computation that the underlying hardware does:

  • Memory is a flat array of bytes addressed by integers.
  • Every value has a fixed size in bytes.
  • You can take the address of almost anything with &.
  • You can ask "what is at this address?" with *.
  • Function calls push a frame on the stack and return pops it.

Compare to Python, where a + b may involve attribute lookups, type coercions, reference counts, and a method dispatch through a hash table. In C, a + b for two ints usually compiles to a single CPU instruction.

Code Block
C 17 (201710L)

What C gives you that other languages do not

CapabilityCPython / JS / Java
Take the address of a variableyes (&x)no
Lay out memory exactly as you wantyes (struct, union)no
Talk to hardware registersyes (volatile pointers, MMIO)no
Run with no operating systemyes (freestanding mode)no
Predictable, jitter-free latencyyes (no GC)no
Compile to almost any chipyesusually no

What C doesn't give you

A pragmatic course must be honest about C's costs:

  • No memory safety. Out-of-bounds writes are undefined behavior.
  • No bounds-checked strings or arrays. You manage lengths yourself.
  • No built-in containers. You write or import a vector / hash map.
  • No namespaces / modules. Just #include and the linker.
  • No exceptions. Errors are usually returned as int codes.
  • Very little type safety for void pointers. void* casts are easy.
  • Undefined behavior is everywhere. Signed overflow, reading uninitialized memory, strict aliasing violations — the compiler is allowed to do anything in response.

Undefined behavior is not just "crashes"

"Undefined behavior" (UB) means the C standard imposes no requirements. The program might crash, print garbage, behave correctly on one machine and wrong on another, or — most dangerously — appear to work in testing and fail in production. A serious chunk of this course is teaching you to recognize and avoid UB.

Where C shines today

You should reach for C (or a C-compatible language) when you need:

  1. Predictable performance. No GC pauses, no JIT warm-up.
  2. Tiny binaries. Embedded systems with kilobytes of flash.
  3. Direct hardware access. Drivers, firmware, OS kernels.
  4. A stable ABI. Almost every language can call C, so C is the universal glue for FFI (ctypes, JNI, P/Invoke, Node addons).
  5. Long-lived infrastructure. Code written in C in 1990 still compiles and runs today.

A taste of "systems thinking" in C

The program below prints the size of every fundamental type on this machine. The answers are not standardized — they depend on the target architecture. On the WASI target you are about to run, ints are 32 bits and pointers are 32 bits.

Code Block
C 17 (201710L)

That single program tells you a great deal about the target: integer widths, pointer width (and therefore the size of the address space), and floating-point representation. We will return to these numbers constantly.

Test your understanding

QuestionSelect one

Which of the following is not a typical reason to choose C over a higher-level language like Python or Java?

Predictable execution with no garbage collector pauses

Ability to run on a microcontroller with kilobytes of memory

Direct access to hardware addresses and registers

Built-in safety against out-of-bounds memory access

QuestionSelect one

What does "undefined behavior" (UB) mean in the C standard?

The behavior is platform-specific but always well defined per platform.

The program will reliably crash with a clear error message.

The standard imposes no requirements: the compiler may produce any code at all, including code that appears to work in testing.

It is only a problem in debug builds; optimizers ignore it.

QuestionSelect one

Why is C often called "portable assembly"?

Because every C compiler emits the same assembly language.

Because C exposes the underlying machine model — flat addressable memory, fixed-size values, and direct pointer arithmetic — while still letting you retarget the compiler to many CPUs.

Because C programs are written in mnemonics like MOV and ADD.

Because every C statement maps to exactly one CPU instruction.

On this page