如何查看C预处理器输出?

发布于 2024-09-19 20:22:08 字数 80 浏览 3 评论 0原文

在将 C 预处理器生成的输出转换为目标文件之前,如何查看它?

我想看看宏定义对我的代码做了什么。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

羞稚 2024-09-26 20:22:08
gcc -E file.c

或者

g++ -E file.cpp

将为您做这件事。 -E 开关强制编译器在预处理阶段后停止,将当前所有内容吐出到标准输出。

注意:当然你必须有一些#include 指令。包含的文件也会经过预处理,因此您可能会获得大量输出。

对于 Visual C++,开关是 /E,它将预处理器输出发送到屏幕。

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.

超可爱的懒熊 2024-09-26 20:22:08

您也可以直接调用 C 预处理器。

cpp infile outfile

查看 man cpp 了解更多信息。

You can also call the C Preprocessor directly.

cpp infile outfile

Check out man cpp for more info.

笑看君怀她人 2024-09-26 20:22:08

对于GCC来说,

gcc -E -dM file.c

还是

g++ -E -dM file.cpp

应该做这个工作。 -dM,正如 GNU 预处理器手册 所说的,应该生成一个列表“#define”指令用于在预处理器执行期间定义的所有宏,包括预定义的宏。

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.

留一抹残留的笑 2024-09-26 20:22:08

这取决于您使用的编译器。
使用 GCC,您可以在命令行上指定 -E 标志,让编译器生成预处理器输出。

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.

嗫嚅 2024-09-26 20:22:08

-save-temps

-E 相比,此选项的一大优点是可以很容易地将其添加到任何构建脚本中,而不会干扰构建本身有很多内容:

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

main.c

#define INC 1

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

,现在,除了正常输出 main.o 之外,当前工作目录还包含以下文件:

  • main.i是一个包含所需的预处理文件:

    <前><代码>#1“main.c”
    #1“<内置>”
    # 1 “<命令行>”
    # 31 “<命令行>”
    # 1 “/usr/include/stdc-predef.h” 1 3 4
    # 32 “<命令行>” 2
    # 1 “main.c”

    int myfunc(int i) {
    返回 i + 1;
    }

  • main.s 是一个额外的好处,并且包含所需的生成程序集:

    <前><代码> .文件“main.c”
    。文本
    .globl myfunc
    .type myfunc,@function
    我的函数:
    .LFB0:
    .cfi_startproc
    推q%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
    添加$1,%eax
    弹出%rbp
    .cfi_def_cfa 7, 8
    雷特
    .cfi_endproc
    .LFE0:
    .size myfunc, .-myfunc
    .ident“GCC:(Ubuntu 8.3.0-6ubuntu1)8.3.0”
    .section .note.GNU-stack,"",@progbits

-save-temps=obj

如果您想为大型项目执行此操作,文件数量,请考虑使用:

-save-temps=obj

它将中间文件保存到与 -o 对象输出相同的目录,而不是当前工作目录,从而避免潜在的基本名称冲突。

例如:

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

导致文件的创建:

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

显然,苹果计划接管世界。

-save-temps -v

关于此选项的另一个很酷的事情是,如果您添加 -v

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

它实际上显示正在使用的显式文件,而不是/tmp 下的丑陋临时文件,因此很容易确切地知道发生了什么,其中包括预处理/编译/汇编步骤:

/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

在 Ubuntu 22.10 amd64、GCC 8.3.0 中测试。

Visual Studio Code 鼠标悬停宏展开

将鼠标悬停在宏上会自动展开它们,它往往工作得很好!不确定是否需要正确的跨文件引用,因为我通常使用 clangd: VSCode“转到定义”不起作用

Git 源代码文件中的示例 remote.c 直接打开源目录后如下:

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

当我将鼠标悬停在 ALL_REV_FLAGS 上时宏:

在此处输入图像描述

然后我还可以在悬停弹出窗口上选择文本,其中包含:

#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))

因此我们看到它既提供了其他宏方面的顶级扩展,也提供了完整的最终递归扩展。

同样在这种情况下,我们看到它跨文件工作,因为在这种情况下,定义来自文件 修订版.h

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

谈情不如逗狗 2024-09-26 20:22:08

如果使用 Jetbrains 的 CLion,您可以使用“clangd:预处理当前 TU”操作

,因此点击 shift shift 并开始输入 clangd...

action popup

最佳将其分配给快捷方式,以便在首选项 -> 键映射中更简单地重用:

在此处输入图像描述

marcosbento

PS:TU 表示“翻译单元”(请参见此处 LLVM 翻译单元)

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)

兲鉂ぱ嘚淚 2024-09-26 20:22:08

您可以查看此处描述的我的脚本:

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

它将预处理器输出格式化为(希望)可读的 html 文档:由于预处理器而不同的行被标记在文件。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文