C
C is the language the computing world is built on. Operating system kernels (Linux, Windows, macOS's core), language runtimes (Python, Ruby, PHP are C programs), databases (PostgreSQL, SQLite, Redis), Git, curl, OpenSSL — the load-bearing layer of software is overwhelmingly C, five decades after its creation.
C gives you almost nothing — no classes, no garbage collector, no strings beyond byte arrays — and in exchange hides almost nothing: what you write maps closely to what the machine executes. That transparency is why C remains the lingua franca of systems programming, embedded devices, and every language's foreign-function interface, and why learning it permanently changes how you understand higher-level languages.
TL;DR
- C is a small, compiled language with manual memory management — you
mallocand youfree, and every bug class between them is yours. - Pointers are the core concept: variables that hold memory addresses. Arrays, strings, and "pass by reference" are all pointer arithmetic underneath.
- The compile → link model:
.cfiles compile to object files; the linker joins them and libraries into an executable. Headers (.h) declare, source files define. - Undefined behavior (UB) is C's sharpest edge — out-of-bounds access, use-after-free, and signed overflow don't necessarily crash; they silently corrupt.
- Strings are
chararrays terminated by'\0'— half of historical security vulnerabilities trace to mishandling this. - Modern C is written with sanitizers on (
-fsanitize=address,undefined) and warnings as errors (-Wall -Wextra -Werror).
Quick Example
The essentials in one program — structs, heap allocation, pointers:
Core Concepts
Pointers
A pointer stores an address; * dereferences it, & takes one:
Everything characteristic about C flows from this: functions receive copies of arguments, so to modify the caller's data you pass a pointer; an array name decays to a pointer to its first element; p[i] is literally *(p + i). Grasp pointers and the rest of C is bookkeeping.
Memory: Stack vs Heap
The ownership question — who frees this, and when? — has no compiler enforcement in C. Disciplined codebases answer it with conventions (create/destroy function pairs, ownership comments); Rust exists largely to make the compiler enforce it. See Memory Management for the deeper treatment.
Strings
A C string is a char array ending in '\0'. The standard library trusts that terminator absolutely:
This single pattern — unchecked copies into fixed buffers — powered decades of exploits. Modern C uses length-bounded functions (snprintf, strncpy with manual termination) and treats every external input as hostile.
The Compilation Model
Headers exist because each .c file compiles in isolation: util.h declares int parse(const char *s); so main.c can call it; the linker later connects the call to the definition in util.o. Build systems (make, CMake) exist to orchestrate exactly this graph — see Build Tools.
Undefined Behavior
C's contract: break certain rules and the compiler owes you nothing — not a crash, not an error, any behavior at all:
The practical defense is tooling, not vigilance:
Sanitizers turn silent corruption into loud, located crashes. No serious C development runs without them.
Where C Fits Today
C vs Its Neighbors
New low-level projects increasingly weigh Rust for its safety guarantees; C remains unavoidable for embedded targets, kernel work, existing codebases, and any library that must be callable from every other language.
Common Mistakes
Returning Pointers to Stack Memory
Return heap memory (documented: caller frees), take a caller-supplied buffer, or use static storage (with its thread-safety cost).
Ignoring Return Values
malloc returns NULL on failure; fopen, read, snprintf all report errors through return values C makes easy to ignore. Robust C checks them — there are no exceptions coming to save you.
sizeof on a Pointer
Array length must travel with the pointer (a struct, or a separate parameter). Only true arrays in scope know their size.
Rolling Your Own Bounds "Checking"
Off-by-one errors around < vs <= and the null terminator are C's bread-and-butter bugs. Centralize buffer handling in a few well-tested helpers rather than inlining strcpy logic everywhere.
FAQ
Should I learn C in the 2020s?
If you work anywhere near systems — embedded, OS, performance, security, language internals — yes, it's mandatory literacy. Even for pure application developers, a few weeks of C demystifies pointers, memory, and what your garbage collector actually does. As a first production language for web work, no — see Go or Python.
C or C++?
C++ is (roughly) C plus classes, templates, RAII, and a vast standard library — more safety tools, far more complexity. Embedded and kernel work leans C; game engines and large performance-critical applications lean C++. They interoperate closely.
Is C dying because of Rust?
No — the installed base is oceanic, embedded toolchains are C-first, and C is the ABI every language binds to. What's changing: new security-sensitive infrastructure increasingly starts in Rust, and even the Linux kernel now accepts Rust drivers. Expect decades of coexistence.
Which C standard should I use?
C11 or C17 are the safe modern defaults (C23 is current but toolchain support varies by target). Embedded projects often pin older standards for compiler compatibility — follow the platform.
How do I avoid C's security pitfalls?
Compile with -Wall -Wextra -Werror, develop with AddressSanitizer/UBSan, fuzz anything that parses input, use length-bounded string functions, and treat every buffer size as a contract. Tooling catches what discipline misses.
Related Topics
- C++ — C plus abstraction
- Rust — Memory safety without garbage collection
- Memory Management — Stack, heap, and ownership in depth
- Systems Programming — The domain C defines
- Go — The garbage-collected systems alternative
- Build Tools — make, CMake, and the compile/link graph
- WebAssembly — Running C in the browser
References
- The C Programming Language (Kernighan & Ritchie) — The classic
- Modern C (Jens Gustedt) — Free, current best practices
- SEI CERT C Coding Standard — Security rules
- cppreference — C documentation