【问题标题】:Preprocessor only on arbitrary file?预处理器只在任意文件上?
【发布时间】:2019-12-06 19:59:18
【问题描述】:

我想证明预处理器完全独立于构建过程。它是不同于 C 语言的另一种语法和词法分析器。事实上,我想表明预处理器可以应用于任何类型的文件。

所以我有这个任意文件:

#define FOO    
#ifdef FOO    
I am foo    
#endif    
#
#
#    
Something    
#pragma Hello World

我认为这会起作用:

$ gcc -E test.txt -o-
gcc: warning: test.txt: linker input file unused because linking not done

不幸的是,它只适用于这个:

$ cat test.txt | gcc -E -

为什么 GCC 会出现这个错误?

【问题讨论】:

  • 直接使用cpp 对您来说重要吗?或者这对您的演示没有实际意义?

标签: c gcc clang c-preprocessor


【解决方案1】:

你需要告诉gcc这是一个C文件。

gcc -xc -E test.txt

【讨论】:

    【解决方案2】:

    您可以使用 C 预处理器,cpp(或更传统的形式,/lib/cpp):

    cpp test.txt
    

    /lib/cpp test.txt
    

    【讨论】:

      【解决方案3】:

      C 编译器使用文件名后缀作为必须编译的文件的指示符(以.c 结尾)只需要链接的文件(以.o.so 结尾)以.s 结尾的它调用汇编器as(1),对于以.f 结尾的文件,它调用fortran 编译器,对于.cc,它切换到C++ 编译。

      确实,通常,C 编译器会将它们不匹配的所有内容都作为链接器文件,因此一旦您将链接器文件传递给它,它就会尝试链接它,并调用链接器ld(1)。这就是您的.txt 文件所发生的情况。链接器有一些类似的方法来识别 ld(1) 脚本对对象或共享对象文件。

      顺便说一句,CPP 语言确实是一种宏语言,但与 C 有一些无法避免的相似之处。它至少可以识别 C 标识符,因为宏名称与 C 标识符具有相同的语法,并且它必须检查标识符是否与宏名称匹配。另一方面......它必须识别 C cmets 和 C 字符串(它确实消除了编译器的 cmets),因为宏扩展不会在它们内部展开,并且它还必须识别括号(它们被认为是宏参数检测和,符号,用于分隔参数)。它还识别(在宏字符串内)标记 #(将参数字符串化)和 ##(将两个符号连接并合并为一个)(最后一个运算符必须强制 cpp 识别几乎任何 C 标记,因为它如果您尝试将+##+ 之类的内容合并到++ 中,则必须检查错误,这是一个错误)

      因此,结论是: cpp 不必将整个 C 语法实现为一种语言,但 C 语言的标记必须被识别几乎完全。 C 语言的标准强制 c 预处理器对输入进行标记,因此## 运算符可用于合并标记(并检查有效性)这意味着,如果您定义如下宏:

      #define M(p) +p
      

      然后你这样称呼它:

      a = +M(-c);
      

      你会得到一个类似的字符串:

      a = + +-c;
      

      在输出中(它将在两个 + 符号之间插入一个空格,因此它们不会合并到 ++ 运算符中。符号 +- 是在一起的,因为它们永远不会被扫描为一个令牌)参见下一个示例(输入前面有>符号)

      $ cpp - <<EOF
      > #define M(p) +p
      > a = +M(p);
      > b = -M(p);
      > p = +M(+p);
      > p = +M(-p);
      > EOF
      # 1 "<stdin>"
      # 1 "<built-in>" 1
      # 1 "<built-in>" 3
      # 346 "<built-in>" 3
      # 1 "<command line>" 1
      # 1 "<built-in>" 2
      # 1 "<stdin>" 2
      
      a = + +p;
      b = -+p;
      p = + + +p;
      p = + +-p;
      

      另一个示例将显示解析标记的更多困难(输入用&gt; 分隔,stderr 用&gt;&gt; 分隔,stdout 不加引号):

      $ cpp - <<EOF
      #define M(a,b) a##b
      > a = M(a+,+b)
      > a = M(a+,-b)
      > a = M(a,+b)
      > a = M(a,b)
      > a = M(a,300)
      > a = M(a,300.2)
      > EOF
      >> <stdin>:3:5: error: pasting formed '+-', an invalid preprocessing token
      >> a = M(a+,-b)
      >>     ^
      >> <stdin>:1:17: note: expanded from macro 'M'
      >> #define M(a,b) a##b
      >>                 ^
      >> <stdin>:4:5: error: pasting formed 'a+', an invalid preprocessing token
      >> a = M(a,+b)
      >>     ^
      >> <stdin>:1:17: note: expanded from macro 'M'
      >> #define M(a,b) a##b
      >>                 ^
      >> <stdin>:7:5: error: pasting formed 'a300.2', an invalid preprocessing token
      >> a = M(a,300.2)
      >>     ^
      >> <stdin>:1:17: note: expanded from macro 'M'
      >> #define M(a,b) a##b
      >>                 ^
      >> 3 errors generated.
      # 1 "<stdin>"
      # 1 "<built-in>" 1
      # 1 "<built-in>" 3
      # 346 "<built-in>" 3
      # 1 "<command line>" 1
      # 1 "<built-in>" 2
      # 1 "<stdin>" 2
      
      a = a++b
      a = a+-b
      a = a+b
      a = ab
      a = a300
      a = a 300.2
      

      正如您在此示例中所见,合并 a300 很好,因为一个令牌生成一个标识符,这是有效的,cpp(1) 不会抱怨,但是当合并 a 和 @987654353 @ 生成的标记 a300.2 不是 C 中的有效标记,因此它被拒绝(它也没有连接,并且工具插入了一个空格,以使编译器将两个标记视为单独的 --- 是否应该将两者连接在一起,它们会被扫描为令牌a300.2)。

      如果您想使用独立于语言的宏预处理器,请考虑使用m4(1) 作为宏语言。它在很多方面都比cpp 强大得多。但请注意,由于它允许的宏扩展的复杂性,它很难学习。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-09-25
        • 1970-01-01
        • 2019-06-22
        • 1970-01-01
        • 2019-06-29
        • 2013-04-30
        • 1970-01-01
        • 2022-01-01
        相关资源
        最近更新 更多