Last updated: March 2025

How to Compile and Run a C Program (Linux, Mac & Windows) — Step-by-Step Guide

Before you can run a C program, you have to compile it — translate your human-readable source code into machine code your CPU can execute. This guide walks you through that process completely: installing a compiler, understanding what the compiler actually does at each stage, running your first program on Linux, macOS, or Windows, and fixing the errors you'll almost certainly encounter along the way.

Advertisement
1

Install GCC

GCC (GNU Compiler Collection) is the most widely-used C compiler in the world. It is free, open-source, and available on every major platform. Here is how to install it on each OS.

Ubuntu / DebianShell
# Update package list and install GCC
sudo apt update
sudo apt install gcc

# Verify installation
gcc --version
gcc (Ubuntu 13.2.0) 13.2.0
Fedora / RHEL / CentOSShell
sudo dnf install gcc
Arch LinuxShell
sudo pacman -S gcc

macOS does not ship GCC by default, but Apple's Xcode Command Line Tools include clang — a fully compatible C compiler — aliased as gcc.

macOS TerminalShell
# Install Xcode Command Line Tools (includes clang/gcc)
xcode-select --install

# Confirm — on macOS, gcc is an alias for clang
gcc --version
Apple clang version 15.0.0 (clang-1500.3.9.4)

# For true GCC via Homebrew (optional)
brew install gcc

Windows has two recommended options. WSL is the easiest for beginners; MinGW-w64 gives you a native Windows GCC.

Option A — WSL (Windows Subsystem for Linux) — recommendedPowerShell
# In PowerShell as Administrator:
wsl --install
# Restart, then open Ubuntu from the Start menu and run:
sudo apt update && sudo apt install gcc
Option B — MinGW-w64 (native Windows GCC)Shell
# 1. Download the MinGW-w64 installer from mingw-w64.org
# 2. Run the installer and choose x86_64 architecture
# 3. Add the bin\ folder to your Windows PATH
# 4. Open Command Prompt and verify:
gcc --version
gcc (x86_64-win32-seh-rev0) 13.2.0
Advertisement
2

Write Your First C Program

Create a file called hello.c using any text editor (VS Code, Vim, Nano, Notepad++). The .c extension tells the compiler this is C source code.

hello.cC
#include <stdio.h>   // Standard I/O library

int main() {
    printf("Hello, World!\n");
    return 0;   // 0 = success; OS receives this value
}
What each line does: #include <stdio.h> tells the preprocessor to include the standard I/O library (which defines printf). int main() is the entry point — every C program starts here. printf writes text to the terminal. return 0 tells the OS the program finished successfully (any non-zero value signals an error).
Explore Now → Learn More
3

Compile with GCC

Open your terminal, navigate to the folder containing hello.c, and run:

terminalShell
# Basic compile — creates an executable called 'hello'
gcc hello.c -o hello

# Recommended: add warnings and debug info
gcc -Wall -Wextra -g hello.c -o hello

Breaking down the flags:

  • hello.c — the input source file
  • -o hello — name the output executable hello (without -o, GCC defaults to a.out)
  • -Wall — enable all common warnings (highly recommended)
  • -Wextra — enable additional warnings beyond -Wall
  • -g — include debug symbols (needed if you use gdb or valgrind)
If you see no output after running gcc, that's good. GCC is silent on success. Output only appears when there are errors or warnings. To confirm the executable was created, run ls -l hello (Linux/macOS) or dir hello.exe (Windows).
Advertisement
4

Run the Executable

Linux / macOS terminalShell
./hello
Hello, World!

The ./ prefix means "run the file named hello in the current directory." It is required on Unix-like systems because the current directory is not in $PATH by default.

Windows Command PromptShell
hello.exe
Hello, World!

REM Or just:
hello

On Windows, GCC produces a .exe file. The current directory is in PATH by default in Command Prompt, so you can run it without a path prefix.

Check the exit code: On Linux/macOS, run echo $? immediately after your program exits. If it prints 0, your return 0 reached the OS correctly. On Windows, use echo %ERRORLEVEL%. This is how shell scripts detect whether a program succeeded or failed.

What the Compiler Actually Does: The 4 Stages

When you run gcc hello.c -o hello, GCC doesn't do everything in one step. It runs four separate programs internally — each one transforming your code further toward executable machine code. Understanding these stages helps you diagnose errors and use GCC's advanced options correctly.

📝
Preprocessing
cpp
hello.i
⚙️
Compilation
cc1
hello.s
🔩
Assembly
as
hello.o
🔗
Linking
ld
hello

Stage 1 — Preprocessing

The preprocessor (cpp) handles all lines starting with #. It expands #include by inserting the full contents of the header file, replaces #define macros with their values, and evaluates #ifdef / #endif conditionals. The output is pure C text with no preprocessor directives remaining.

stop at preprocessing stageShell
gcc -E hello.c -o hello.i   # Produces expanded C text
wc -l hello.i
    741 hello.i               # stdio.h expands to 741 lines!

Stage 2 — Compilation

The compiler proper (cc1) translates preprocessed C into assembly language — a human-readable representation of CPU instructions specific to the target architecture (x86-64, ARM, RISC-V, etc.).

stop at compilation stage — inspect assemblyShell
gcc -S hello.c -o hello.s   # Produces assembly (.s file)
cat hello.s                 # View the generated x86-64 assembly

Stage 3 — Assembly

The assembler (as) converts the assembly text into binary object code — a .o file containing machine instructions and a symbol table, but with external references (like printf) still unresolved.

stop at assembly stageShell
gcc -c hello.c -o hello.o   # Produces object file
file hello.o
hello.o: ELF 64-bit LSB relocatable, x86-64

Stage 4 — Linking

The linker (ld) combines your .o file with library object files (like the C standard library libc) and resolves all unresolved symbols. The result is a complete, standalone executable the OS can load and run.

Why does this matter? When you get a undefined reference to 'printf' error, it happens at the linking stage — not compilation. You've written valid C, but the linker can't find the library that contains printf. The fix is usually to add -lm (for math functions) or ensure the standard library is linked (GCC does this automatically for C, but not always for custom libraries).
Advertisement

Useful GCC Flags You Should Know

GCC has dozens of options. These are the ones every C programmer uses regularly:

FlagPurposeWhen to use
-o <name>Set output file nameAlways — avoids the default a.out
-WallEnable common warningsAlways — catches many bugs before runtime
-WextraExtra warnings beyond -WallRecommended for all non-trivial code
-WerrorTreat warnings as errorsProduction builds, CI/CD pipelines
-gInclude debug symbolsDuring development; required for gdb/valgrind
-O2Optimization level 2 (fast, safe)Release builds where speed matters
-O0No optimizationDebugging — optimized code reorders lines
-std=c11Use C11 standardWhen using C11 features (threads, atomics)
-std=c99Use C99 standardEmbedded/legacy compatibility
-lmLink math libraryWhen using sqrt(), pow(), etc.
-EStop after preprocessingDebugging macro expansions
-SStop after compilation (output assembly)Learning / low-level optimization
-cCompile only, no linkingMulti-file projects, Makefiles
-fsanitize=addressAddressSanitizer — detects memory errorsTesting for buffer overflows, use-after-free
recommended development commandShell
# The command most professional C developers use during development:
gcc -Wall -Wextra -g -std=c11 -fsanitize=address hello.c -o hello
Advertisement

Common Compilation Errors and How to Fix Them

Every C beginner encounters the same set of errors. Here are the most common ones with their exact error messages, causes, and fixes:

implicit declaration of function

Cause: Using a function without including the right header.
Fix: Add the missing #include. E.g. printf needs #include <stdio.h>, sqrt needs #include <math.h>.

undefined reference to '...'

Cause: Linker can't find a function. Usually a missing library flag.
Fix: For math functions add -lm: gcc prog.c -o prog -lm. For custom files, compile all .c files together.

expected ';' before '}'

Cause: Missing semicolon at end of a statement.
Fix: Add ; after the statement on the line before the reported line (errors often point one line late).

use of undeclared identifier

Cause: Variable or function used before it is declared.
Fix: Declare the variable before using it, or move the function prototype above the call site.

no such file or directory

Cause: The .c file path is wrong, or the header file doesn't exist.
Fix: Check the filename and path. Run ls *.c to confirm the file exists in the current directory.

Segmentation fault (runtime)

Cause: Accessing invalid memory — null pointer, out-of-bounds array, freed memory.
Fix: Compile with -g -fsanitize=address, re-run, and AddressSanitizer will report the exact line.
Advertisement

Compiling C Online (No Installation Needed)

If you cannot install GCC right now, or just want to experiment quickly, several free online C compilers let you write, compile, and run C programs directly in your browser.

Compiler Explorer

godbolt.org

Shows live assembly output as you type. Best for understanding what the compiler does.

OnlineGDB

onlinegdb.com

Full online IDE with step-through debugger. Best for beginners learning to debug.

Programiz

programiz.com

Clean, simple interface. Best for quick "run this snippet" checks.

Replit

replit.com

Full browser IDE with file system. Best for multi-file projects without local setup.

Online compilers are fine for learning, but for serious development you'll want a local GCC installation — online compilers have input/output limitations, can't access the file system, and don't support all GCC flags or libraries.
Advertisement

Frequently Asked Questions

Write your C code in a .c file, then run gcc hello.c -o hello to compile it. Run the result with ./hello on Linux/macOS or hello.exe on Windows. If GCC produces no output, the compilation succeeded and your executable is ready.
The -o flag sets the name of the output executable. Without it, GCC names the output a.out on Linux/macOS or a.exe on Windows — a generic name that gets overwritten every time you compile. Always use -o yourprogramname to give each executable a meaningful name.
"Undefined reference" is a linker error, not a compiler error. It means the linker can't find the binary implementation of a function. Common causes: (1) you split your code across multiple .c files but only compiled one of them — compile all of them: gcc main.c utils.c -o app; (2) you're using a math function like sqrt() but forgot -lm; (3) you declared but never defined a function.
The 4 stages are: (1) Preprocessing — expands #include and #define directives. (2) Compilation — translates C to assembly language. (3) Assembly — converts assembly to binary object code (.o file). (4) Linking — combines object files and libraries into the final executable. GCC runs all four automatically unless you use -E, -S, or -c to stop early.
List all .c source files in the GCC command: gcc main.c utils.c math.c -o app. GCC compiles each file separately and then links them together. For larger projects, use a Makefile to only recompile files that have changed, rather than recompiling everything every time.
A compiler error (or linker error) is detected before the program runs. GCC reports it and does not produce an executable. Examples: syntax errors, wrong types, undefined references. A runtime error occurs while the program is running. Examples: segmentation fault (invalid memory access), division by zero, stack overflow. Runtime errors are harder to catch — use -g -fsanitize=address to detect memory errors and valgrind for deeper analysis.
Advertisement

Continue learning C on CoodeVerse

CoodeVerse Editorial Team

The CoodeVerse editorial team consists of experienced software developers and educators specializing in C, Python, Java, and web development. All content is technically reviewed and updated regularly.