101

How do I view the output produced by the C pre-processor, prior to its conversion into an object file?

I want to see what the MACRO definitions do to my code.

1

7 Answers 7

128
gcc -E file.c

or

g++ -E file.cpp

will do this for you. The -E switch forces the compiler to stop after the preprocessing phase, spitting all it’s got at the moment to standard output.

Note: Surely you must have some #include directives. The included files get preprocessed, too, so you might get lots of output.

For Visual C++ the switch is /E which spits the preprocessor output to screen.

4
  • 50
    gcc -E -nostdinc will bypass standard #include files
    – pmg
    Sep 18, 2010 at 18:03
  • 3
    You might also need to specify -x c if the file you are processing has not a standard c extension
    – Antonio
    Apr 13, 2017 at 9:26
  • 2
    You might need to specify -I, and -D switches.
    – x-yuri
    Feb 28, 2018 at 19:11
  • 5
    The -P flag will Inhibit the generation of linemarkers. Sep 17, 2020 at 22:40
26

You can also call the C Preprocessor directly.

cpp infile outfile

Check out man cpp for more info.

1
  • 1
    Nice! I prefer this one over cc -E. Also, I use -P to disable line markets too (works with cc as well).
    – mtmk
    Feb 21, 2020 at 18:13
13

For GCC,

gcc -E -dM file.c

or

g++ -E -dM file.cpp

should do the job. -dM, as GNU Preprocessor manual puts it, should generate a list of ‘#define’ directives for all the macros defined during the execution of the preprocessor, including predefined macros.

7

It depends on the compiler you use.
With GCC, you can specify the -E flag on the command-line to let the compiler produce the pre-processor output.

5

-save-temps

The big advantage of this option over -E is that it is very easy to add it to any build script, without interfering much in the build itself:

gcc -save-temps -c -o main.o main.c

main.c

#define INC 1

int myfunc(int i) {
    return i + INC;
}

and now, besides the normal output main.o, the current working directory also contains the following files:

  • main.i is a contains the desired preprossessed file:

    # 1 "main.c"
    # 1 "<built-in>"
    # 1 "<command-line>"
    # 31 "<command-line>"
    # 1 "/usr/include/stdc-predef.h" 1 3 4
    # 32 "<command-line>" 2
    # 1 "main.c"
    
    
    int myfunc(int i) {
        return i + 1;
    }
    
  • main.s is a bonus, and contains the desired generated assembly:

        .file   "main.c"
        .text
        .globl  myfunc
        .type   myfunc, @function
    myfunc:
    .LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    %edi, -4(%rbp)
        movl    -4(%rbp), %eax
        addl    $1, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
    .LFE0:
        .size   myfunc, .-myfunc
        .ident  "GCC: (Ubuntu 8.3.0-6ubuntu1) 8.3.0"
        .section    .note.GNU-stack,"",@progbits
    

-save-temps=obj

If you want to do it for a large number of files, consider using instead:

-save-temps=obj

which saves the intermediate files to the same directory as the -o object output instead of the current working directory, thus avoiding potential basename conflicts.

For example:

gcc -save-temps -c -o out/subdir/main.o subdir/main.c

leads to the creation of files:

out/subdir/main.i
out/subdir/main.o
out/subdir/main.s

Clearly an Apple plot to take over the world.

-save-temps -v

Another cool thing about this option is if you add -v:

gcc -save-temps -c -o main.o -v main.c

it actually shows the explicit files being used instead of ugly temporaries under /tmp, so it is easy to know exactly what is going on, which includes the preprocessing / compilation / assembly steps:

/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu main.c -mtune=generic -march=x86-64 -fpch-preprocess -fstack-protector-strong -Wformat -Wformat-security -o main.i
/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -fpreprocessed main.i -quiet -dumpbase main.c -mtune=generic -march=x86-64 -auxbase-strip main.o -version -fstack-protector-strong -Wformat -Wformat-security -o main.s
as -v --64 -o main.o main.s

Tested in Ubuntu 22.10 amd64, GCC 8.3.0.

Visual Studio Code mouse hover macro expansion

Hovering over macros automatically expands them, it tends to work very well! Not sure if proper cross file referencing is needed or not, for that I normally use clangd: VSCode "go to definition" not working

Example from the Git source code file remote.c on vscode 1.87.1, C/C++ extension v1.19.6, Ubuntu 23.10 after opening the source directory directly as:

git clone https://github.com/git/git
cd git
git checkout e09f1254c54329773904fe25d7c545a1fb4fa920
code .

as I hover over the ALL_REV_FLAGS macro:

enter image description here

I can then also select text on the hover popup, which contains:

#define ALL_REV_FLAGS (((1u<<11)-1) | NOT_USER_GIVEN | TRACK_LINEAR | PULL_MERGE)

Expands to:

(((1u<<11)-1) | (1u<<25) | (1u<<26) | (1u<<15))

So we see that it gives both the toplevel expansion in terms of other macros, as well as the full final recursive expansion.

Also in this case we see that it worked across files, as in this case the definition comes from file revision.h.

1
4

If using CLion by Jetbrains, you can use the action "clangd: Preprocess current TU"

So hit shift shift and start typing clangd...

action popup

Best assign it to a shortcut for simpler reuse in preferences->keymap:

enter image description here

Shout out to marcosbento

PS: TU means 'translation unit' (see here LLVM translation unit)

2

You can check out my script described here:

http://mosermichael.github.io/cstuff/all/projects/2011/09/16/preprocessor.html

It formats the preprocessor output into a (hopefully) readable html document: lines that are different due to preprocessor are marked in the file.

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.