为什么包含使用完整路径的标头会导致更好的错误消息?

mur*_*uru 13 c gcc compiler-errors

最近有一篇关于Ask Ubuntu的文章,其中OP试图编写一个包括的程序term.h.当代码有#include <term.h>,错误是:

In file included from clear_screen_UNIX.c:5:0:
clear_screen_UNIX.c:9:6: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘->’ token
 void clear_screen(void) {
      ^
clear_screen_UNIX.c: In function ‘main’:
clear_screen_UNIX.c:23:14: error: called object is not a function or function pointer
  clear_screen();
              ^
clear_screen_UNIX.c:26:14: error: called object is not a function or function pointer
  clear_screen();
Run Code Online (Sandbox Code Playgroud)

然后,OP包含了term.h(#include "/usr/include/term.h")的完整路径,这导致了更有用的消息:

In file included from clear_screen_UNIX.c:7:0:
/usr/include/term.h:125:21: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘->’ token
 #define CUR cur_term->type.
                     ^
/usr/include/term.h:202:40: note: in expansion of macro ‘CUR’
 #define clear_screen                   CUR Strings[5]
                                        ^
clear_screen_UNIX.c:9:6: note: in expansion of macro ‘clear_screen’
 void clear_screen(void) {
      ^
clear_screen_UNIX.c: In function ‘main’:
clear_screen_UNIX.c:23:14: error: called object is not a function or function pointer
  clear_screen();
              ^
clear_screen_UNIX.c:26:14: error: called object is not a function or function pointer
  clear_screen();
Run Code Online (Sandbox Code Playgroud)

这些消息清楚地表明问题是由于宏观扩张造成的.

我自己也验证了结果.我想知道为什么GCC在给出完整路径时会产生更好的错误.我可以使用系统包含语法生成类似的消息吗?

我正在使用GCC 4.9.2,我怀疑OP正在使用GCC 4.8.2(考虑到Ubuntu的版本).

Kun*_*ing 9

结论

如果给出头的完整路径,GCC给出不同/更好的消息的原因是GCC预处理器向GCC的cc1编译器提供信息 ,包含的头是系统头文件或本地头文件,在注释行的末尾有一些数字预处理器生成的.i文件.

然后,cc1如果头文件是本地头文件,编译器将生成更多有用的消息,并且如果头文件是系统头,则将根据GCC文档抑制一些错误消息.

为了使代码输出错误消息的正常版本就像指定头文件的完整路径的代码一样,GCC需要通过指定选项来停止包含所有系统目录-nostdinc,然后明确告诉GCC目录它可以搜索头文件,而不使用-I标志将目录视为系统目录.

对于您的代码,命令行可能如下所示(GCC_INCLUDE_DIR是您的默认GCC包含目录,对于系统默认GCC,它可能是/usr/lib/gcc/x86_64-unknown-linux-gnu/4.9.2/include/):

gcc -c t.c -nostdinc -I/usr/include/ -IGCC_INCLUDE_DIR
Run Code Online (Sandbox Code Playgroud)

源代码

这个原始帖子移动源代码,使这个答案更有帮助.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <term.h>

//#include "/usr/include/term.h"

void clear_screen(void) {
    if (!cur_term) {
        int result;
        setupterm( NULL, STDOUT_FILENO, &result );
    if (result <= 0)
        return;
    }

    putp( tigetstr( "clear" ) );
}

int main(void) {
    puts("I am going to clear the screen");
    sleep(1);
    clear_screen();
    puts("Screen Cleared");
    sleep(1);
    clear_screen();

    return 0;

}
Run Code Online (Sandbox Code Playgroud)

预处理器生成的文件之间的区别

您可以使用以下命令行来请求GCC输出preproceser生成的代码.这段代码将被输入GCC的实际编译器中cc1.如果预处理器生成的文件完全相同,则cc1编译器的行为应该完全相同.(假设代码被放入文件中t.c)

 gcc -E t.c -o t.i
Run Code Online (Sandbox Code Playgroud)

以下是两个gcc预处理器生成的.i文件之间的区别.这t.fullpath.i是使用完整路径头文件生成的文件,而t.i没有完整路径的代码(某些diff输出已被删除,因为它们只是文件名差异.)

$ diff t.i t.fullpath.i
2920,2922c2920,2924
< # 1 "/usr/include/term.h" 1 3 4
< # 47 "/usr/include/term.h" 3 4
---
> # 1 "/usr/include/term.h" 1
> # 47 "/usr/include/term.h"
2924,2925c2926,2927
< # 48 "/usr/include/term.h" 2 3 4
< # 80 "/usr/include/term.h" 3 4
---
> # 48 "/usr/include/term.h" 2
> # 80 "/usr/include/term.h"
3007,3008c3009,3010
< # 81 "/usr/include/term.h" 2 3 4
< # 673 "/usr/include/term.h" 3 4
---
> # 81 "/usr/include/term.h" 2
> # 673 "/usr/include/term.h"
3041c3043
< # 729 "/usr/include/term.h" 3 4
---
> # 729 "/usr/include/term.h"
Run Code Online (Sandbox Code Playgroud) 预处理器中生成的代码注释中的不同标志会产生差异

GCC的cc1编译器将利用预处理器生成的信息来生成错误消息的源代码位置,以及将来用于gdb的调试信息.

对于以下格式:

# line-number "source-file" [flags]
Run Code Online (Sandbox Code Playgroud)

数字34标志表示:

  • 3:以下文本来自系统头文件(#include <>vs #include "")
  • 4:以下文本应被视为包含在隐式extern "C"块中.

    有关这些标志的不同类型的更多信息,请参阅此链接.

因此,对于没有完全指定路径的代码,cc1编译器会将其视为系统头文件,并假设系统代码大部分是正确的,然后只输出用户代码的错误消息.这就是错误消息更短的原因.