What Is a Compiler? A Plain Explanation for CS Students
A compiler is a program that translates source code written in a high-level language, such as C, Rust, or Go, into machine code a processor can execute directly. It reads the whole program, checks it for errors, and produces a standalone executable. That’s the short version. The interesting part is what happens in between, and it’s exactly what exams and interviews tend to probe.
What a Compiler Actually Does
Processors only understand binary instructions, and nobody wants to write those by hand. So you write readable code, and a translator handles the conversion before the program ever runs.
That timing matters. Because translation happens ahead of time, a syntax error shows up at build time, not halfway through execution. Think of it as translating a whole book before publishing, instead of interpreting live at a conference.
How Compilation Works: The Six Stages
- Lexical analysis. The lexer splits raw text into tokens: keywords, identifiers, operators, and numbers.
- Parsing. The parser checks tokens against the language grammar and builds an abstract syntax tree (AST).
- Semantic analysis. Types and scopes get verified. Assigning a string to an integer variable fails here.
- Intermediate representation. The code becomes a neutral form, such as LLVM IR, that isn’t tied to one CPU.
- Optimization. Dead code disappears, constants get folded, and loops get reshaped.
- Code generation. The backend emits machine code for the target architecture. A linker then merges object files and libraries into one executable.
Compiler vs. Interpreter
An interpreter runs code at runtime, usually statement by statement, with no separate executable. A compiler pays the translation cost once; an interpreter pays it on every run. Yet the line is blurrier than most textbooks admit. CPython compiles your script to bytecode first, then a virtual machine runs it. Java works similarly, and the JVM’s just-in-time (JIT) compilation turns frequently used bytecode into machine code mid-execution.
| Compiler | Interpreter | |
|---|---|---|
| Translates | Before running | While running |
| Output | Executable file | None |
| Errors surface. | Build time | Often runtime |
| Common examples | GCC, rustc | Shells, early BASIC |
Compilers You’ll Actually Use
GCC and Clang are the big two for C and C++. Clang sits on LLVM, the framework that also powers rustc and Swift’s toolchain. Java’s javac produces bytecode rather than native code. Want to see real output? Run gcc -S hello.c and open the assembly file. It’s messy. It’s also illuminating.
Common Misconceptions
Compiled doesn’t automatically mean faster. A sloppy algorithm in C loses to a smart one in Python. And a compiler isn’t an assembler: an assembler only converts assembly into machine code, which is one late step in the pipeline.
I’m also simplifying. Production compilers run dozens of optimization passes, and stages vary by language.