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

时间:2015-04-19 23:08:08

标签: 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();

然后,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();

这些消息清楚地表明问题是由于宏扩展造成的。

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

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

1 个答案:

答案 0 :(得分:9)

结论

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

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

要使代码输出错误消息的正常版本与指定头文件的完整路径的代码一样,如您所要求的那样,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

源代码

this original post移动源代码,使这个答案更有帮助。

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

}

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

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

 gcc -E t.c -o t.i

以下是两个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"
预处理器生成的代码注释中的不同标志有所不同

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

对于以下格式:

# line-number "source-file" [flags]

标志的数字34表示:

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

    有关这些旗帜的不同类型的更多信息,请参阅this link

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