【问题标题】:Why does including a header using the full path lead to better error messages?为什么包含使用完整路径的标头会导致更好的错误消息?
【发布时间】:2015-06-26 12:47:28
【问题描述】:

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 的版本)。

【问题讨论】:

  • 对我来说看起来像一个 gcc 错误
  • @user2225104 老实说,我不经常用 C 编写代码(并且通过扩展,使用 GCC 或其他 C 编译器)来了解这里的预期行为。
  • 如果你改用 llvm/clang 会发生什么?
  • @uchuugaka 不知道,我手头没有带有clang的系统。有的话我会更新的。

标签: c gcc compiler-errors


【解决方案1】:

结论

如果给出标头的完整路径,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 输出预处理器生成的代码。这段代码将被输入到 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"
预处理器生成的代码的 cmets 中的不同标志会有所不同

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

对于以下格式:

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

标志的数字34表示:

  • 3:以下文本来自系统头文件(#include &lt;&gt; vs #include ""
  • 4:以下文本应被视为包含在隐式 extern "C" 块中。

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

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

【讨论】:

  • 很有趣,但 diff 输出难道不是相反的吗? (文件名旁边的数字被删除 - 信息更少!)O.o 那么我可以在某处使用标志来使 cpp 包含更多信息吗?
  • 这是有道理的。所以除了使用完整路径之外,没有办法让cpp 跳过这些标志?
  • 不,-Wsystem-headers 没有区别。
  • 抱歉消息不正确,您应该使用-nostdinc -ISYSTEM_DIR 选项。有关详细信息,请参阅我上述答案的最新版本。 @muru
  • 对我来说仍然像一个错误哈哈。相比之下,我认为像-allow_cryptic_error_messages_for_standard_headers 这样的开关是一个经过深思熟虑的功能:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-28
  • 2014-09-10
  • 2023-01-27
  • 2020-03-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多