Turning a .c file into a runnable program actually happens in four distinct stages, even though a single gcc command hides them from you.
gcc -E main.c -o main.i # 1. Preprocessing: expands #include and macros
gcc -S main.i -o main.s # 2. Compilation: translates C into assembly
gcc -c main.s -o main.o # 3. Assembly: turns assembly into machine code (object file)
gcc main.o -o app # 4. Linking: combines object files + libraries into an executable
./app # run it
In practice, you almost never run these separately — gcc -o app main.c does all four steps in one command. But understanding them explains real error categories: a missing header causes a preprocessing error, invalid syntax causes a compilation error, and a missing function definition (like forgetting to link a library) causes a linking error — each shows up differently and is diagnosed differently.
Always compile with warnings enabled:
gcc -std=c17 -Wall -Wextra -Werror -o app main.c
-Wall and -Wextra surface a wide range of likely bugs (unused variables, comparing signed and unsigned values, implicit conversions) that C's permissive design would otherwise silently allow. -Werror treats every warning as a hard error, which is a strong habit for catching mistakes early rather than debugging them later at runtime.