Почему этот вывод препроцессора, по-видимому, не имеет смысла? - PullRequest
0 голосов
/ 11 ноября 2019

Извините за плохой заголовок, надеюсь, мое объяснение яснее.

У меня есть следующая c программа:

clang_test.c

#include "clang_test2.c"
int main()
{
    somefunc();
    return 0;
}

clang_test2.c

int somefunc()
{
    return 5;
}

Затем я скомпилировал его, используя clang с параметром -E, чтобы увидеть результат препроцессора.

clang.exe -std=c99 -pedantic-errors -E .\clang_test.c

Вывод препроцессора выглядит следующим образом:

# 1 ".\\clang_test.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 324 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 ".\\clang_test.c" 2

# 1 "./clang_test2.c" 1
int somefunc()
{
 return 5;
}
# 3 ".\\clang_test.c" 2

int main()
{
 somefunc();
 return 0;
}

Это работает, как ожидалось, и я не получаю ошибки компиляции, если я пытаюсь скомпилировать его регулярно без -E.

Ради эксперимента я изменил clang_test.c, чтобы не #include clang_test2.c:

int main()
{
     somefunc();
     return 0;
}

Затем я попытался скомпилировать, используя:

clang.exe -std=c99 -pedantic-errors .\clang_test2.c .\clang_test.c

И я получаю сообщение об ошибке компилятора::

.\clang_test.c:13:2: error: implicit declaration of function 'somefunc' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
somefunc();

Но если я посмотрю на вывод препроцессора, похоже, что он должен работать, потому что somefunc() все еще объявлен над функцией main, где он используется:

# 1 ".\\clang_test2.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 324 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 ".\\clang_test2.c" 2
int somefunc()
{
 return 5;
}
# 1 ".\\clang_test.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 324 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 ".\\clang_test.c" 2

int main()
{
 somefunc();
 return 0;
}

Итак, основываясь на этом наблюдении, не является ли надежным анализ выходных данных препроцессора для диагностики проблем, связанных с определениями / объявлениями функций? А также, поскольку единственное различие между двумя выходами препроцессора заключается в следующем текстовом блоке:

# 1 ".\\clang_test.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 324 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 ".\\clang_test.c" 2

Что можно сделать, чтобы предотвратить правильное объявление somefunc()?

1 Ответ

1 голос
/ 11 ноября 2019

Строка

clang.exe -std=c99 -pedantic-errors .\clang_test2.c .\clang_test.c

не просто объединяет два файла и компилирует их. Он компилирует каждый из файлов C отдельно в объектные (.o) файлы и затем связывает их. Вы получаете ошибку, потому что скомпилированная одна

int main()
{
    somefunc();
    return 0;
}

не определяет somefunc. Вам понадобится прототип, чтобы сообщить компилятору его тип:

int somefunc(void);

int main(void)
{
    somefunc();
    return 0;
}

Обратите внимание, что вы должны использовать правильные прототипы повсюду. Функции без аргументов должны быть объявлены с помощью void списков аргументов. Интерфейсы функций старого стиля (например, со списками аргументов ()) допускают многие виды ошибок, которые прототипы позволяют обнаруживать компилятору.

С другой стороны, вывод компилятора с -E

clang.exe -std=c99 -pedantic-errors -E .\clang_test2.c .\clang_test.c

делает объединение двух файлов и отправку их через препроцессор C. Разница - компиляция в объект и симпатия к конкатенации - объясняет поведение, которое вы видите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...