Menu
Coddy logo textTech

What Is a Compiler?

A compiler is a program that translates source code written in a programming language into machine code, or into another lower-level form such as bytecode, before the program runs. The translated program can then run without the original source.

By Kevin Spektor, Co-founder & CTO

Updated September 24, 2026

Source code
  1. 1print("Hello")
  2. 2total = 2 + 3
  3. 3print(total)
  4. 4print("Done")
Output
Times compiled0
Runs0

Compile once, then run the program as many times as you like: the runs counter goes up, the compile counter does not. Turn on the typo and the compiler refuses the whole program, so nothing prints.

Save this C program as hello.c:

#include <stdio.h>

int main(void) {
    int price = 4;
    printf("Total: %d\n", price * 3);
    return 0;
}

Then type gcc hello.c -o hello. Nothing visible happens for a moment, then a new file called hello appears next to your source. That file is the compiler's output: machine code your processor can run. Delete hello.c and ./hello still works, because everything the program needs has already been translated.

gcc hello.c -o hello
./hello
Total: 12

How a compiler works, step by step

A compiler reads source code as plain text and works through it in stages. Take the single line total = price * 3:

  1. Lexical analysis. The text is split into tokens, the "words" of the language: the name total, the operator =, the name price, the operator *, the number 3. Spaces and comments are dropped here.
  2. Parsing. The tokens are arranged into a syntax tree that records the structure: an assignment whose value is a multiplication of price and 3. Code that breaks the grammar of the language stops here with a syntax error.
  3. Semantic analysis. The compiler checks meaning: is price declared, are the types compatible, does the function being called exist.
  4. Optimization. The program is rewritten to run faster or use less memory without changing what it does. x * 2 may become a single shift instruction, and code that can never run is removed.
  5. Code generation. The compiler writes the output for a target: x86-64 or ARM machine code, or bytecode for a virtual machine.

For C and C++ a final step, linking, joins your compiled files with library code (such as printf from the C standard library) into one executable. gcc runs the linker for you, which is why it feels like one command.

Python ships the first two stages as standard modules, so you can watch them work on real input:

NAME 'total'
OP '='
NAME 'price'
OP '*'
NUMBER '3'
Assign(
  targets=[
    Name(id='total', ctx=Store())],
  value=BinOp(
    left=Name(id='price', ctx=Load()),
    op=Mult(),
    right=Constant(value=3)))

That tree is what the rest of the compiler works from. It no longer cares about spaces or line breaks, only about the structure: assign to total the result of multiplying price by 3.

Compile time: errors found before the program runs

Because a compiler reads the whole program before any of it runs, it can reject mistakes early. In this example none of the three print lines inside source runs, not even the first one, because Python's compiler refuses the text before execution starts:

Rejected before running anything:
  '(' was never closed (line 3)

Compilers for statically typed languages catch more than syntax. This line of C, in a file called bad.c, is grammatically correct, but it stores text in an integer variable:

int count = "five";

GCC 14 and newer stop with an error:

bad.c:4:17: error: initialization of 'int' from 'char *' makes integer from pointer without a cast [-Wint-conversion]

The same mistake in Python would only surface when that line runs. Catching it at compile time is one reason large teams choose compiled, statically typed languages. A compiler cannot catch everything, though: dividing by a number that turns out to be zero, or reading a file that does not exist, still fails at run time.

Examples of compilers

CompilerReadsProduces
GCC, ClangC, C++Machine code for x86-64, ARM and others
MSVC (Microsoft Visual C++)C, C++Windows executables (.exe, .dll)
javacJavaJVM bytecode (.class files)
rustcRustMachine code
go buildGoA single executable file
tscTypeScriptJavaScript

Two rows show that "machine code" is not the only possible output. javac produces bytecode that the Java Virtual Machine runs, part of the Java runtime environment. tsc turns TypeScript into JavaScript, another high-level language; a compiler that does this is often called a transpiler.

Compiled machine code is tied to one CPU family and one operating system. A program compiled for Windows on x86-64 does not run on a Mac with an Apple M-series chip; you compile the same source again for each target.

Compiler or interpreter

An interpreter runs source code directly instead of producing a separate program file, which is how Python and Ruby are usually run. Most modern language tools use both ideas: CPython compiles your file to bytecode and then interprets it, and the JVM and JavaScript's V8 engine compile frequently used code to machine code while the program is running. The full side-by-side table is on the compiler vs interpreter page.

Common misconceptions

  • "If it compiles, it works." A compiler checks that the program is valid, not that it is correct. Logic mistakes and run-time failures such as division by zero get through.
  • "Python is not compiled." CPython compiles every file to bytecode before running it. The .pyc files in __pycache__ folders are that bytecode, saved for the modules your program imports so the next run can skip the step.
  • "An online compiler is a compiler." Sites that run your code in the browser send it to a server that runs the full toolchain (compiler, linker, and the program itself) and returns the output. The compiler is one piece of that.
  • "Compiled code runs anywhere." It runs on the CPU and operating system it was built for. Portability comes from recompiling, or from a virtual machine such as the JVM.

Where to go next

The C docs show a compiler in daily use: how to compile and run C walks through preprocessing, compiling, assembling and linking with real gcc commands. To see the other way of running code, read compiler vs interpreter, then runtime environment for what surrounds a program once it starts. The C course is a good place to practice with a compiled language.

Frequently Asked Questions

What are the components of a compiler?
A compiler is usually described in three parts. The front end reads the source code, splits it into tokens, parses it into a syntax tree and checks types. The middle end optimizes an intermediate form of the program, and the back end generates machine code or bytecode for the target CPU.
How can a compiler compile itself?
Through bootstrapping. The first version of a compiler is written in an existing language, or compiled by an older compiler, and is then used to compile the next version written in the new language itself. GCC, for example, is written mostly in C and C++ and is built with an existing C++ compiler, often an earlier GCC.
What is the difference between a compiler and an assembler?
A compiler translates a high-level language such as C or Rust, where one line can become many machine instructions. An assembler translates assembly language, where each line maps almost one to one to a single machine instruction. Compilers such as GCC often produce assembly first and hand it to an assembler.
What is a JIT compiler?
A just-in-time (JIT) compiler translates code into machine code while the program is running, instead of before. It watches which parts run most often and compiles those, so a long-running program gets faster as it goes. The JVM, JavaScript's V8 engine and PyPy all use JIT compilation.
Do I need to install a compiler to learn programming?
Not at first. Browser-based editors and online playgrounds run the compiler or interpreter on a server for you. When you move to your own machine, C and C++ need a compiler such as GCC or Clang, Java needs a JDK, and Python needs the Python interpreter.
Coddy programming languages illustration

Learn to code with Coddy

GET STARTED