Подтвердить что ты не робот

Почему включение заголовка с использованием полного пути приводит к улучшению сообщений об ошибках?

Недавно появилась надпись 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).

4b9b3361

Ответ 1

Заключение

Причина, по которой 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

Исходный код

Переместил исходный код здесь из этого оригинального сообщения, чтобы сделать этот ответ более полезным.

#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"
Различный флаг в препроцессоре генерирует комментарии к коду, что делает разницу Компилятор

GCC cc1 будет использовать информацию, защищенную препроцессором, для генерации местоположения исходного кода сообщения об ошибке, а также отладочную информацию, которая будет использоваться для gdb в будущем.

Для следующего формата:

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

Знаки 3 и 4 флагов означают:

  • 3: Следующий текст поступает из файла заголовка системы (#include <> vs #include "")
  • 4: Следующий текст следует рассматривать как обернутый в неявный блок extern "C".

    Дополнительные сведения о различных типах этих флагов см. в этой ссылке.

Следовательно, для кода без полностью указанного пути cc1 компилятор будет рассматривать его как системный заголовочный файл и предположим, что системный код в основном верен, а затем просто выводит сообщение об ошибке кода пользователя. Вот почему сообщение об ошибке короче.