Question
How can I generate assembler output from C or C++ source code using GCC?
If I want to analyze how a program is being compiled, what command should I use to view the emitted assembly code?
Short Answer
By the end of this page, you will understand how to ask GCC to stop after compilation and write the generated assembly to a file. You will also learn the most useful command-line flags for producing readable assembly, how optimization levels affect the output, and how developers inspect assembly when debugging performance or compiler behavior.
Concept
GCC turns C or C++ source code into machine code through several stages:
- Preprocessing – handles
#include,#define, and conditional compilation - Compilation – translates source code into assembly
- Assembly – converts assembly into object code
- Linking – combines object files and libraries into an executable
If you want to inspect the assembly that GCC generates, you usually want to stop the build process after the compilation stage and save the assembly text to a file.
The main flag for this is:
gcc -S file.c
or for C++:
g++ -S file.cpp
The -S option tells GCC:
- compile the source
- generate assembly output
- do not assemble it into an object file
This matters because assembly is often the easiest way to see:
- how high-level code maps to CPU instructions
- whether functions were inlined
- whether loops were optimized
- how variables are stored or passed
- how different optimization flags change the generated code
In real programming, inspecting assembly is useful for:
- performance tuning
- understanding compiler optimizations
- debugging unexpected low-level behavior
- comparing different code patterns
- learning how C/C++ features work under the hood
Mental Model
Think of GCC like a factory pipeline.
- Your C/C++ source file is the raw material.
- GCC passes it through several machines.
- One machine produces a human-readable low-level blueprint: assembly.
- Another machine turns that blueprint into binary object code.
Using -S is like telling the factory:
“Stop after creating the blueprint, and give me that file instead of the final product.”
That blueprint helps you understand exactly what GCC plans to tell the CPU to do.
Syntax and Examples
The core command is:
gcc -S program.c
This creates a file named:
program.s
For C++:
g++ -S program.cpp
Example 1: Basic C example
Source file:
int add(int a, int b) {
return a + b;
}
Compile to assembly:
gcc -S add.c
Output file:
add.s
Example 2: Choose the output filename
gcc -S main.c -o main.asm
This writes the assembly to main.asm instead of the default main.s.
Example 3: Include optimization
gcc -S -O2 main.c
This generates assembly after applying -O2 optimizations. This is important because optimized assembly can look very different from unoptimized output.
Example 4: Add debugging-friendly annotations
gcc -S -fverbose-asm main.c
-fverbose-asm may include extra comments in the assembly output, which can make it easier to read.
Example 5: Keep source-level debugging information
gcc -S -g main.c
This adds debug information that can help when comparing assembly with source-level tools.
Example 6: Generate Intel syntax instead of AT&T syntax
gcc -S -masm=intel main.c
By default, GCC often emits AT&T syntax on many platforms. If you prefer Intel syntax, -masm=intel is often easier to read for beginners.
Combined example
gcc -S -O2 -fverbose-asm -masm=intel main.c -o main.s
This command:
- compiles
main.c - applies
-O2optimizations - emits assembly
- uses Intel syntax
- adds verbose comments
- writes output to
main.s
Step by Step Execution
Consider this file:
int square(int x) {
return x * x;
}
Command:
gcc -S square.c
What happens step by step
- GCC reads
square.c. - It preprocesses the file.
- It compiles the code into assembly instructions.
- Because you used
-S, GCC stops there. - It writes the assembly text into
square.s.
Likely generated file
The exact output depends on your platform, GCC version, and optimization settings, but it may look roughly like this:
.file "square.c"
.text
.globl square
.type square, @function
square:
imull %edi, %edi
movl %edi, %eax
ret
How to read it at a high level
square:marks the function label.imullmultiplies the input by itself.movlplaces the result into the return register.retreturns from the function.
Important note
You should not expect assembly output to be identical across systems. It changes based on:
- CPU architecture
- calling convention
- optimization level
- GCC version
- target platform
Real World Use Cases
Developers inspect GCC assembly output in many practical situations.
Performance tuning
You want to know whether a loop was optimized well:
for (int i = 0; i < n; i++) {
sum += data[i];
}
Looking at assembly can show whether GCC:
- unrolled the loop
- used registers efficiently
- removed unnecessary memory accesses
Comparing code styles
You want to compare two implementations:
x * 8
versus
x << 3
Assembly output can show whether the compiler generates the same instructions.
Understanding inlining
If a small helper function disappears from assembly at -O2, GCC may have inlined it.
Debugging low-level behavior
If a variable seems to vanish in a debugger, assembly can reveal whether it was optimized away.
Learning how C/C++ works
Assembly helps you see how:
- function calls are made
- parameters are passed
- return values are handled
- structs or classes affect generated code
Real Codebase Usage
In real projects, developers usually do not inspect assembly for every file. Instead, they generate it selectively when investigating something specific.
Common patterns
- Check one file only during optimization work:
gcc -S -O2 hot_path.c - Compare optimization levels:
gcc -S -O0 hot_path.c -o hot_path_O0.s gcc -S -O3 hot_path.c -o hot_path_O3.s - Use Intel syntax for readability:
gcc -S -masm=intel hot_path.c - Preserve source context with extra annotations:
gcc -S -g -fverbose-asm hot_path.c
How teams typically use this
- investigate why a function is slow
- verify that a branch was optimized away
- inspect generated code for embedded systems
- compare compiler versions
- check whether a
const,inline, or template change affects code generation
Related workflow tools
Developers also often combine assembly generation with:
objdump -dto disassemble compiled object files or executables- Compiler Explorer to compare output quickly
-O0,-O2,-O3to study optimization effects-cwhen they want object files instead of assembly
Useful habit
When analyzing output, keep the example small. A short source file makes the generated assembly much easier to understand.
Common Mistakes
Here are common beginner mistakes when trying to get assembly output from GCC.
Mistake 1: Using -c instead of -S
gcc -c main.c
This creates an object file like main.o, not assembly.
Use this instead:
gcc -S main.c
Mistake 2: Expecting the same assembly on every machine
Assembly depends on the target architecture.
For example:
- x86-64 output differs from ARM output
- Linux output may differ from Windows output
- optimization settings change everything
Mistake 3: Forgetting optimization flags
gcc -S main.c
This usually gives unoptimized output, which may be much more verbose than production builds.
If you want realistic optimized code, try:
gcc -S -O2 main.c
Mistake 4: Confusing assembly syntax styles
Many beginners expect Intel syntax, but GCC often emits AT&T syntax by default.
If needed, switch syntax:
gcc -S -masm=intel main.c
Mistake 5: Trying to inspect a huge file first
Large files generate a lot of assembly. Start with a small focused example.
Mistake 6: Assuming source lines map one-to-one to assembly lines
One line of C/C++ may become:
- many assembly instructions
- no visible instructions at all if optimized away
- reordered instructions for performance
Broken expectation example
Source:
int f() {
int x = 5;
return x;
}
A beginner might expect to see a variable stored in memory, but optimized assembly may just return 5 directly.
Comparisons
| Goal | GCC Option | What it Does |
|---|---|---|
| Generate assembly text | -S | Stops after compilation and writes assembly output |
| Generate object file | -c | Stops after assembly and writes .o file |
| Build executable | no special stop flag | Runs full pipeline through linking |
-S vs -c
| Option | Output | Human-readable? | Typical use |
|---|---|---|---|
-S | .s | Yes | Inspect compiler-generated assembly |
-c | .o | No | Build object files for linking |
Unoptimized vs optimized assembly
| Mode | Command | Result |
|---|---|---|
| Unoptimized | gcc -S -O0 file.c | Easier to map to source, but verbose |
| Optimized | gcc -S -O2 file.c | Closer to real production output |
AT&T vs Intel syntax
| Syntax | Default in GCC on many systems | Example style |
|---|---|---|
| AT&T | Yes | %eax, %edi, source before destination |
| Intel | No, use -masm=intel | eax, edi, destination before source |
Source-to-assembly tools compared
| Tool | Best for |
|---|---|
gcc -S | Generate assembly directly from source |
objdump -d file.o | Disassemble compiled object files |
| Compiler Explorer | Quick online inspection and comparison |
Cheat Sheet
# C source to assembly
gcc -S file.c
# C++ source to assembly
g++ -S file.cpp
# Choose output file
gcc -S file.c -o file.s
# Optimized assembly
gcc -S -O2 file.c
# No optimization
gcc -S -O0 file.c
# Intel syntax
gcc -S -masm=intel file.c
# Extra comments in output
gcc -S -fverbose-asm file.c
# Include debug info
gcc -S -g file.c
Key rules
-Smeans stop after generating assembly.-cmeans stop after creating object code.- No stop flag means GCC usually continues through linking.
- Assembly output depends on platform, optimization, and compiler version.
- Use
g++for C++ files when you want normal C++ compilation behavior.
Common file extensions
- C source:
.c - C++ source:
.cpp,.cc,.cxx - Assembly output:
.s - Object file:
.o
Good analysis commands
gcc -S -O0 example.c -o example_O0.s
gcc -S -O2 example.c -o example_O2.s
Compare the two files to see optimization effects.
FAQ
How do I generate assembly from a C file in GCC?
Use:
gcc -S file.c
This creates file.s.
How do I generate assembly from a C++ file in GCC?
Use:
g++ -S file.cpp
This generates assembly using C++ compilation rules.
What is the GCC flag for assembly output?
The main flag is -S.
Why does GCC generate a .o file instead of a .s file?
You probably used -c instead of -S. -c creates an object file.
How can I make GCC assembly easier to read?
Useful options include:
-masm=intel -fverbose-asm
These can switch syntax and add comments.
Why does my assembly look different from someone else’s?
Assembly output changes based on:
- target CPU
- operating system
- GCC version
- optimization flags
Should I inspect -O0 or -O2 assembly?
Use both for different goals:
-O0is easier for learning-O2is better for understanding realistic optimized code
Can I get assembly from an already compiled binary?
Yes, but that is usually done with tools like objdump -d, not gcc -S.
Mini Project
Description
Create a small experiment that shows how GCC turns simple C code into assembly, and how optimization changes the result. This project helps you practice generating .s files and comparing emitted code for the same function under different compiler settings.
Goal
Generate and compare assembly output for a simple C program using both unoptimized and optimized GCC builds.
Requirements
- Write a small C program with at least two functions.
- Generate assembly output with
-O0and save it to one file. - Generate assembly output with
-O2and save it to a different file. - Use a command that writes assembly output to explicit filenames.
- Compare the two assembly files and identify at least one visible difference.
Keep learning
Related questions
2D Array Loop Order and Cache Performance in C
Learn why swapping nested loops changes 2D array performance in C, using row-major memory layout, cache locality, and practical benchmarks.
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.