如何显示';预处理';代码忽略GCC中的includes

如何显示';预处理';代码忽略GCC中的includes,c,linux,gcc,c-preprocessor,C,Linux,Gcc,C Preprocessor,我想知道是否可以使用gcc输出“预处理”代码,但“忽略”(不扩展)包括: 是的,我有一个主要问题: #include <stdio.h> #define prn(s) printf("this is a macro for printing a string: %s\n", s); int int(){ char str[5] = "test"; prn(str); return 0; } 我只想输出: #include <stdio.h> int int(){ c

我想知道是否可以使用gcc输出“预处理”代码,但“忽略”(不扩展)包括:

是的,我有一个主要问题:

#include <stdio.h>
#define prn(s) printf("this is a macro for printing a string: %s\n", s);

int int(){
char str[5] = "test"; 
prn(str);
return 0;
}
我只想输出:

#include <stdio.h>
int int(){
char str[5] = "test";
printf("this is a macro for printing a string: %s\n", str);
return 0;
}

PS:如果可以扩展“本地”
包含,而不扩展“全局”
包含,那就太好了。当cpp扩展包含时,它会将错误追溯到原始文件

您可以添加一个后期处理步骤(可以用任何脚本语言编写,如果您愿意,甚至可以用C编写),以仅解析行标记并过滤掉来自项目目录之外的文件的行;更好的是,其中一个标志(3)标记了系统头文件(来自通过
-issystem
提供的路径的内容,可以是编译器驱动程序隐式的,也可以是显式的),因此您也可以利用它

例如,在Python 3中:

#!/usr/bin/env python3
import sys

skip = False
for l in sys.stdin:
    if not skip:
        sys.stdout.write(l)
    if l.startswith("# "):
        toks = l.strip().split(" ")
        linenum, filename = toks[1:3]
        flags = toks[3:]
        skip = "3" in flags
使用gcc-efoo.c |./filter.py

# 1 "foo.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "foo.c"
# 1 "/usr/include/stdio.h" 1 3 4



# 4 "foo.c"
int int(){
char str[5] = "test";
printf("this is a macro for printing a string: %s\n", str);;
return 0;
}
#1“foo.c”
# 1 ""
# 1 ""
# 31 ""
#1“/usr/include/stdc predef.h”1 3 4
#1“foo.c”
#1“/usr/include/stdio.h”1 3 4
#4“foo.c”
int(){
字符str[5]=“测试”;
printf(“这是一个用于打印字符串的宏:%s\n”,str);;
返回0;
}

假设文件名为
c.c

gcc -E c.c | tail -n +`gcc -E c.c | grep -n -e "#*\"c.c\""  | tail -1 | awk -F: '{print $1}'`
似乎
#“c.c”
会在每个
#include
之后标记行

当然,您也可以将
gcc-ec.c
保存在一个文件中,以避免重复两次


优点是在执行
gcc-E
之前,不修改源代码,也不删除
#include
,只删除从顶部到最后由
#include
生成的所有行。。。如果我是对的

我同意Matteo Italia的评论,即如果您只是阻止扩展
#include
指令,那么生成的代码将不代表编译器实际看到的内容,因此它在故障排除中的作用有限

这里有一个解决这个问题的办法。在include前后添加变量声明。任何合理唯一的变量都可以

int begin_includes_tag;
#include <stdio.h>
... other includes
int end_includes_tag;
sed
命令将删除这些变量声明之间的所有内容。

保护
#include
s不被扩展,以文本方式运行预处理器,删除
#1”“
等。文本预处理器生成垃圾,并重新扩展受保护的
#include
s

此shell函数执行以下操作:

expand_cpp(){
     sed 's|^\([ \t]*#[ \t]*include\)|magic_fjdsa9f8j932j9\1|' "$@" \
     | cpp | sed 's|^magic_fjdsa9f8j932j9||; /^# [0-9]/d'
}
只要你把include这个词放在一起,而不是做一些疯狂的事情,比如

#i\
ncl\
u??/
de <iostream>
expand_cpp
expand_cpp example.c
一样,它生成:

#include <stdio.h>


int int(){
char str[5] = "test";
printf("this is a macro for printing a string: %s\n", str);;
return 0;
}
#包括
int(){
字符str[5]=“测试”;
printf(“这是一个用于打印字符串的宏:%s\n”,str);;
返回0;
}

您可以使用
-dI
显示
#include
指令并对预处理器输出进行后处理

假设文件名为
foo.c

SOURCEFILE=foo.c
gcc -E -dI "$SOURCEFILE" | awk '
    /^# [0-9]* "/ { if ($3 == "\"'"$SOURCEFILE"'\"") show=1; else show=0; }
    { if(show) print; }'
或者要抑制
$SOURCEFILE
的所有
#行编号“文件”
行:

SOURCEFILE=foo.c
gcc -E -dI "$SOURCEFILE" | awk '
    /^# [0-9]* "/ { ignore = 1; if ($3 == "\"'"$SOURCEFILE"'\"") show=1; else show=0; }
    { if(ignore) ignore=0; else if(show) print; }'

注意:AWK脚本不适用于包含空格的文件名。要使用空格处理文件名,您可以修改AWK脚本以比较
$0
,而不是
$3

以前的许多答案都指向使用跟踪
指令

它实际上是经典Unix中的一行程序(使用
awk
):

gcc-E file.c|awk'/#[1-9][0-9]*“file.c/{skip=0;next}/#[1-9][0-9]*”*“/{skip=1}(skipTL;DR

将文件名分配给
fname
并在shell中运行以下命令。在整个过程中,假定
fname
是包含要处理的源文件的
sh
变量

fname=file_to_process.c ;
grep -G '^#include' <./"$fname" ;
grep -Gv '^#include[ ]*<' <./"$fname" | gcc -x c - -E -o - $(grep -G '^#include[ ]*<' <./"$fname" | xargs -I {} -- expr "{}" : '#include[ ]*<[ ]*\(.*\)[ ]*>' | xargs -I {} printf '-imacros %s ' "{}" ) | grep -Ev '^([ ]*|#.*)$'
注意:一般来说,gcc不会扩展打算作为宏的标记,但是缺少定义。然而,
断言
恰好完全扩展:
\uuuuuuu扩展
是编译器选项,
\uuuuu断言
是函数,
\uuuu漂亮的函数
是字符串文字

自动化

以前的方法是可行的,但可能会很乏味

  • 每个
    #include
    都需要手动从文件中删除,并且

  • 它必须作为
    -imacros
    的参数添加到
    gcc
    调用中


  • 第一部分很容易编写脚本:pipe
    grep-Gv'^#include[]*将include封装在
    #ifndef XXXX…#endif
    中,并将
    -dxxx
    -E
    grep-v“#include”一起传递noinclude.c;gcc-E noinclude.c
    如果您使用标准头中的宏,这两种方法都会有问题,因为标准头不会被扩展。@bruno可能可以使用
    sed
    来完成,而不需要中间文件……但我不会考虑它:)您可以随时注释掉系统包含的文件。无需添加实际变量,甚至两条包含GUID的注释都可以。@MatteoItalia谢谢您的想法。我一开始试着使用注释,但很快就意识到注释将被预处理器剥离。我不知道GUID注释不会被剥离。呃,很抱歉,我不知道注释剥离发生在预处理器级别,请忽略我的注释!我会这样做,将“声明标记”放在
    #ifdef DEBUG
    中,因为它很简单,而且我不需要对SED做很多事情(我不熟悉),也因为它允许我轻松地“取消标记”我想要的包process@MatteoItalia,gnu
    cpp
    程序可以进行预处理,同时使用-C标志(或-CC)保留注释,所以我觉得你的技巧很有用。它很有效,只需要添加
    -n
    作为第一个tail的参数,我也喜欢它,因为它的语法清晰易记,而且容易理解adapt@DDS啊,对不起,对我来说,“+”和“-n+”也一样,我还是编辑我的答案。如果我的答案是正确的
    #include <stdio.h>
    
    
    int int(){
    char str[5] = "test";
    printf("this is a macro for printing a string: %s\n", str);;
    return 0;
    }
    
    SOURCEFILE=foo.c
    gcc -E -dI "$SOURCEFILE" | awk '
        /^# [0-9]* "/ { if ($3 == "\"'"$SOURCEFILE"'\"") show=1; else show=0; }
        { if(show) print; }'
    
    SOURCEFILE=foo.c
    gcc -E -dI "$SOURCEFILE" | awk '
        /^# [0-9]* "/ { ignore = 1; if ($3 == "\"'"$SOURCEFILE"'\"") show=1; else show=0; }
        { if(ignore) ignore=0; else if(show) print; }'
    
    fname=file_to_process.c ;
    grep -G '^#include' <./"$fname" ;
    grep -Gv '^#include[ ]*<' <./"$fname" | gcc -x c - -E -o - $(grep -G '^#include[ ]*<' <./"$fname" | xargs -I {} -- expr "{}" : '#include[ ]*<[ ]*\(.*\)[ ]*>' | xargs -I {} printf '-imacros %s ' "{}" ) | grep -Ev '^([ ]*|#.*)$'
    
    printf 'int main(){\nassert(1);\nreturn 0;}\n' | gcc -x c -E - -imacros assert.h | grep -Ev '^([ ]*|#.*)$'
    
    printf '' > std ;
    for header in *.h ; do
        grep -G '^#include[ ]*<' <./$header >> std ;
        sed -i '/#include[ ]*</d' $header ;
    done;
    for source in *.c ; do
        cat std > tmp;
        cat $source >> tmp;
        mv -f tmp $source ;
    done
    
    #ifndef H1H
    #define H1H
    #include <stdio.h>
    #include <limits.h>
    #define H1 printf("H1:%i\n", h1_int)
    int h1_int=INT_MAX;
    #endif
    
    #ifndef H2H
    #define H2H
    #include <stdio.h>
    #include "h1.h"
    #define H2 printf("H2:%i\n", h2_int)
    int h2_int;
    #endif
    
    #include <assert.h>
    #include "h1.h"
    #include "h2.h"
    int main(){
      assert(1);
      H1;
      H2;
    }
    
    fname="$1"
    
    printf '' > std ;
    for source in *.[ch] ; do
        grep -G '^#include[ ]*<' <./$source >> std ;
        sed -i '/#include[ ]*</d' $source ;
        sort -u std > std2;
        mv -f std2 std;
    done;
    for source in *.c ; do
        cat std > tmp;
        cat $source >> tmp;
        mv -f tmp $source ;
    done
    
    grep -G '^#include[ ]*<' <./"$fname" ;
    
    grep -Gv '^#include[ ]*<' <./"$fname" | gcc -x c - -E -o - $(grep -G '^#include[ ]*<' <./"$fname" | xargs -I {} -- expr "{}" : '#include[ ]*<[ ]*\(.*\)[ ]*>' | xargs -I {} printf '-imacros %s ' "{}" ) | grep -Ev '^([ ]*|#.*)$'
    
    #include <assert.h>
    #include <limits.h>
    #include <stdio.h>
    int h1_int=0x7fffffff;
    int h2_int;
    int main(){
     ((void) sizeof ((
     1
     ) ? 1 : 0), __extension__ ({ if (
     1
     ) ; else __assert_fail (
     "1"
     , "<stdin>", 4, __extension__ __PRETTY_FUNCTION__); }))
              ;
      printf("H1:%i\n", h1_int);
      printf("H2:%i\n", h2_int);
    }