Question

How to Generate Assembly Output from C/C++ Source in GCC

ccppassemblygccdisassembly

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:

  1. Preprocessing – handles #include, #define, and conditional compilation
  2. Compilation – translates source code into assembly
  3. Assembly – converts assembly into object code
  4. 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.

Take Quiz

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 -O2 optimizations
  • 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

  1. GCC reads square.c.
  2. It preprocesses the file.
  3. It compiles the code into assembly instructions.
  4. Because you used -S, GCC stops there.
  5. 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.
  • imull multiplies the input by itself.
  • movl places the result into the return register.
  • ret returns 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
Take Quiz

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 -d to disassemble compiled object files or executables
  • Compiler Explorer to compare output quickly
  • -O0, -O2, -O3 to study optimization effects
  • -c when 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

GoalGCC OptionWhat it Does
Generate assembly text-SStops after compilation and writes assembly output
Generate object file-cStops after assembly and writes .o file
Build executableno special stop flagRuns full pipeline through linking

-S vs -c

OptionOutputHuman-readable?Typical use
-S.sYesInspect compiler-generated assembly
-c.oNoBuild object files for linking

Unoptimized vs optimized assembly

ModeCommandResult
Unoptimizedgcc -S -O0 file.cEasier to map to source, but verbose
Optimizedgcc -S -O2 file.cCloser to real production output

AT&T vs Intel syntax

SyntaxDefault in GCC on many systemsExample style
AT&TYes%eax, %edi, source before destination
IntelNo, use -masm=inteleax, edi, destination before source

Source-to-assembly tools compared

ToolBest for
gcc -SGenerate assembly directly from source
objdump -d file.oDisassemble compiled object files
Compiler ExplorerQuick online inspection and comparison
Take Quiz

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

  • -S means stop after generating assembly.
  • -c means 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:

  • -O0 is easier for learning
  • -O2 is 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.

Take Quiz

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

  1. Write a small C program with at least two functions.
  2. Generate assembly output with -O0 and save it to one file.
  3. Generate assembly output with -O2 and save it to a different file.
  4. Use a command that writes assembly output to explicit filenames.
  5. Compare the two assembly files and identify at least one visible difference.
Take Quiz

Keep learning

Related questions

View more