Seeing expanded C macros

后端 未结 12 956
清歌不尽
清歌不尽 2020-11-28 05:31

If I want to expand a C macro, what are some good ways to do that (besides tracing it manually)?

For instance, GTK_WIDGET_SET_FLAGS, it uses a macro that

相关标签:
12条回答
  • 2020-11-28 05:34

    Depending on which compiler you use, there should be a way to see the code after the preprocessor (which does the macro expansion, macros are not known by the compiler at all) is done.

    With gcc, the option is -E. Here's a simplified example, using toy code and not the actual GTK+ macro:

    ~/tmp> cat cpptest.c
    #define SET_FLAGS(w, f) ((w)->flags |= (f))
    
    int main(void)
    {
            SET_FLAGS(0, 4711);
    
            return 0;
    }
    ~/tmp> gcc -E cpptest.c
    # 1 "cpptest.c"
    # 1 "<built-in>"
    # 1 "<command line>"
    # 1 "cpptest.c"
    
    
    int main(void)
    {
     ((0)->flags |= (4711));
    
     return 0;
    }
    
    0 讨论(0)
  • 2020-11-28 05:34

    You can dump the expansion of a macro at run time like this:

    #include <stdio.h>
    
    /*
     * generic helper macros
     */
    #define CALL(macro, arguments) macro arguments
    #define STR(...) STR_(__VA_ARGS__)
    #define STR_(...) # __VA_ARGS__
    
    /*
     * dumps a macro and its expansion to stdout
     * the second argument is optional and specifies the number of
     * arguments that macro takes: 0 means macro takes zero arguments
     * no second argument means macro is not function-like
     */
    #define DUMP_MACRO(macro, ...) \
        do { \
            puts ( \
                "'" \
                # macro STR(DUMP_MACRO_ARGS_ ## __VA_ARGS__) \
                "' expands to '" \
                STR(CALL(macro, DUMP_MACRO_ARGS_ ## __VA_ARGS__)) \
                "'" \
            ); \
        } while (0)
    /* helpers for DUMP_MACRO, add more if required */
    #define DUMP_MACRO_ARGS_
    #define DUMP_MACRO_ARGS_0 ()
    #define DUMP_MACRO_ARGS_1 (<1>)
    #define DUMP_MACRO_ARGS_2 (<1>, <2>)
    #define DUMP_MACRO_ARGS_3 (<1>, <2>, <3>)
    
    /*
     * macros to be used in examples for DUMP_MACRO
     */
    #define EXAMPLE ( EXAMPLE0() << 9 )
    #define EXAMPLE0() __GNUC__
    #define EXAMPLE1(EXAMPLE1) EXAMPLE1
    #define EXAMPLE3(EXAMPLE1, _, __) ( EXAMPLE1 ? _(__) : false )
    
    int main() {
        /* examples */
        DUMP_MACRO(EXAMPLE);
        DUMP_MACRO(EXAMPLE0, 0);
        DUMP_MACRO(EXAMPLE1, 1);
        DUMP_MACRO(EXAMPLE3, 3);
        DUMP_MACRO(EXAMPLE3(EXAMPLE, EXAMPLE1, non_macro_symbol));
        /* does not work for DUMP_MACRO itself, because the
           preprocessor does not allow recursion */
        DUMP_MACRO(DUMP_MACRO, 1);
        DUMP_MACRO(DUMP_MACRO, 2);
        return 0;
    }
    

    The program prints:

    'EXAMPLE' expands to '( 4 << 9 )'
    'EXAMPLE0()' expands to '4'
    'EXAMPLE1(<1>)' expands to '<1>'
    'EXAMPLE3(<1>, <2>, <3>)' expands to '( <1> ? <2>(<3>) : false )'
    'EXAMPLE3(EXAMPLE, EXAMPLE1, non_macro_symbol)' expands to '( ( 4 << 9 ) ? non_macro_symbol : false )'
    'DUMP_MACRO(<1>)' expands to 'DUMP_MACRO (<1>)'
    'DUMP_MACRO(<1>, <2>)' expands to 'DUMP_MACRO (<1>, <2>)'
    

    However this yields only the full expansion. If you need single steps, Eclipse/CDT can help, but only if you teach it all the headers and compiler flags you use.

    0 讨论(0)
  • 2020-11-28 05:34

    You want to run just the preprocessor stage of your compiler, responsible for expanding macros. For gcc, that's "gcc -E", but I'm not sure about other compilers.

    0 讨论(0)
  • 2020-11-28 05:34

    GCC -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.

    When you do:

    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
      

    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.

    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 19.04 amd64, GCC 8.3.0.

    0 讨论(0)
  • 2020-11-28 05:36

    If you use gcc you can also run

    cpp myfile.c
    
    0 讨论(0)
  • 2020-11-28 05:42

    Many IDEs will show you the expanded version of the macro in the editor when the mouse pointer hovers over the identifier (or some other way). I know Eclipse/CDT does this, and Visual Studio does this (at least VS 2008 does).

    Having the compiler generate preprocessed output can be useful if you're tracking down a tricky problem, but for day in/day out use where you just want to know what's going on with the code on your screen,using the IDE is the way to go.

    0 讨论(0)
提交回复
热议问题