The Compilation Process: From Source Code to Executable
After completing this topic, you will be able to:
- Explain the difference between compilers and interpreters.
- Understand the steps involved in converting source code into machine code.
- Know how Python, JavaScript, and C are executed.
Computers Cannot Read Source Code
The code we write is text for humans.
print("Hello, World!")What the CPU understands is machine code β instructions in 0s and 1s.
10110000 01001000 (mov al, 'H')
11001101 00100001 (int 21h)We need to convert source code into machine code. The programs that perform this conversion are called compilers and interpreters.
Compiler vs. Interpreter
| Compiler | Interpreter | |
|---|---|---|
| Conversion Timing | Converts the entire program at once before execution. | Converts and executes line by line. |
| Output | Executable file (binary). | None (executes immediately). |
| Error Detection | Shows all errors at compile time. | Shows errors at the time of executing a specific line. |
| Execution Speed | Faster (already converted). | Relatively slower. |
| Example Languages | C, C++, Rust, Go | Python, JavaScript, Ruby |
Think of it this way: a compiler is like translating a book (translating the whole thing first and then publishing it), and an interpreter is like simultaneous interpretation (translating as you speak).
Compilation Steps: The Case of C
The process of a C code file becoming an executable:
Source Code (.c)
β Preprocessing
Preprocessed Code
β Compilation
Assembly Code (.s)
β Assembly
Object File (.o)
β Linking
Executable File (a.out / .exe)1. Preprocessing
#include <stdio.h>
#define MAX 100
int main() {
printf("Max is %d\n", MAX);
}The #include directive is replaced with the actual header file content, and #define is replaced with its value. This step involves text substitution.
2. Compilation
The preprocessed code is analyzed and converted into assembly code. Syntax errors are detected here.
mov edi, OFFSET FLAT:.LC0
mov esi, 100
call printf3. Assembly
Assembly code is converted into machine code (binary instructions). The result is an object file (.o).
4. Linking
Multiple object files and libraries are combined into a single executable file. The actual implementation of printf is in the C standard library, so the linker connects it.
# GCC performs all these steps in one gogcc hello.c -o hello./helloHow Python is Executed
Python is a hybrid of compilation and interpretation.
Source Code (.py)
β Python Compiler
Bytecode (.pyc)
β Python Virtual Machine (PVM)
Execution Result# Checking the bytecodeimport dis
def add(a, b): return a + b
dis.dis(add)# LOAD_FAST 0 (a)# LOAD_FAST 1 (b)# BINARY_ADD# RETURN_VALUEBytecode is not machine code. The Python Virtual Machine (PVM), which is an interpreter, executes the bytecode line by line. This is slower than the CPU directly executing the code (as in C), but it can run on any operating system.
You may have seen .pyc files appear in the __pycache__/ folder. These are the bytecode caches. If the source code hasn't changed, the cache is used without recompilation.
How JavaScript is Executed
JavaScript engines (V8, SpiderMonkey) use JIT (Just-In-Time) compilation.
Source Code (.js)
β Parsing
AST (Abstract Syntax Tree)
β Interpreter
Bytecode Execution (slow)
β JIT Compiler (detects frequently executed code)
Converted to Machine Code (fast)Initially, it starts executing with an interpreter for quick startup, and when it detects that "this function has been called 1,000 times?", it compiles that part into machine code. Since it compiles during execution, it is called "Just-In-Time".
Java: The Quintessential Hybrid of Compilation and Interpretation
Java is similar to Python, but more complex.
Source Code (.java)
β javac (compilation)
Bytecode (.class)
β JVM (Java Virtual Machine)
βββ Interpreter (initially)
βββ JIT Compiler (for frequently executed code)
β
Machine Code Execution// Hello.java
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}javac Hello.java # Creates bytecode (.class)java Hello # JVM executes the bytecode"Write once, run anywhere" β Java bytecode can run anywhere that has a JVM. A .class file compiled on Windows will also work on a Linux JVM.
Static Typing vs. Dynamic Typing
Type systems are closely related to compilation.
// C: Static typing - Types are checked at compile time
int x = 42;
x = "hello"; // Compile error!# Python: Dynamic typing - Types are checked at runtimex = 42x = "hello" # OK - Type changes during execution| Static Typing | Dynamic Typing | |
|---|---|---|
| Type Checking Time | Compile time | Runtime |
| Error Detection | Before execution | During execution |
| Type Specification | Required (int x) | Not required (x = 42) |
| Execution Speed | Faster | Relatively slower |
| Example Languages | C, Java, TypeScript | Python, JavaScript, Ruby |
Practical Implications
Why is Python slower than C?
β C: The CPU directly executes machine code.
β Python: The virtual machine interprets and executes bytecode line by line.
Why use TypeScript?
β Adds static typing to JavaScript β Catches errors before execution.
β TypeScript β (tsc compilation) β JavaScript β (V8 execution)
Why do Docker images differ for each OS?
β Compiled binaries are dependent on the CPU architecture (x86, ARM).
β A macOS binary will not run on Linux.Key Summary
| Concept | Summary |
|---|---|
| Compiler | Converts the entire source code into machine code in advance. Faster execution. |
| Interpreter | Interprets and executes line by line. Faster development. |
| Bytecode | An intermediate format. Executed by a virtual machine (Python, Java). |
| JIT | Compiles frequently used parts into machine code during execution (JavaScript). |
| Static Typing | Types are checked at compile time (C, TypeScript). |
| Dynamic Typing | Types are checked at runtime (Python, JavaScript). |
Build Tools and Transpilers
In modern development, in addition to "pure" compilation/interpretation, there are various other conversion tools.
TypeScript β (tsc) β JavaScript β (V8) β Execution
JSX/React β (Babel) β JavaScript β (V8) β Execution
Sass/SCSS β (sass) β CSS β Browser RenderingA transpiler converts code into a code at the same level. If a compiler converts high-level to low-level, a transpiler converts high-level to high-level. TypeScript to JavaScript and ES6 to ES5 are examples.
"Python is an interpreted language" is only half true β it compiles to bytecode and then interprets it. "JavaScript is an interpreted language" is also only half true β V8 uses JIT compilation. Modern languages mix compilation and interpretation to achieve both development convenience and execution performance.