GCC делает неявные объявления функций неправильно в режиме c99?
Рассмотрим следующий код:
int main (void) {
int i = xyzzy();
return i;
}
int xyzzy (void) {
return 42;
}
Теперь, хотя прототип для xyyzy
неизвестен в точке использования, это работает в режиме c89, потому что по умолчанию возвращаемый тип функции, у которой нет прототипа, является int
, поэтому неявный прототип функции и фактическая функция совместимы.
И, фактически, если вы измените возвращаемый тип функции на float
, вы получите (как и ожидалось):
testprog.c:6: error: conflicting types for 'xyzzy'
testprog.c:2: error: previous implicit declaration of 'xyzzy' was here
Потому что неявный прототип и фактическая функция больше не существуют. спичка.
Исходный код, скомпилированный с gcc --std=c89 --pedantic -Wall -Wextra
, дает мне только предупреждение:
testprog.c: In function 'main':
testprog.c:2: warning: implicit declaration of function 'xyzzy'
Что вполне ожидаемо, поскольку c89 имеет это сказать в 3.7.1 Function definitions
:
Extern int max (int a, int b) { ... }: здесь
extern
- спецификатор класса хранения, аint
- спецификатор типа (каждый из которых может быть опущен, поскольку они являются значениями по умолчанию).
И в 3.3.2.2 Function calls
:
Таким образом, использование функции перед ее объявлением определенно приводит к созданию прототипа по умолчанию.Если выражение, которое предшествует скобках перечень аргументов в вызов функции состоит исключительно из идентификатора, и если для этого идентификатора не видно объявления, то идентификатор неявно объявляется точно так же, как если бы в самом внутреннем блоке, содержащем появился вызов функции, объявление extern int identifier (); .
Однако обе эти фразы были удалены в c99, и мы вместо этого найти в 6.5.2.2 Function calls
(мой жирный):
Если выражение, обозначающее вызываемую функцию, имеет указатель типа на функцию, возвращающую тип объекта, то выражение вызова функции имеет тот же тип, что и этот тип объекта, и имеет значение, определенное в соответствии с пунктом 6.8.6.4. в противном случае вызов функции имеет тип void.
Я понимаю, что это означает, что если при попытке вызова функции нет объявления, то она неявно объявляется с возвращением void
тип.
gcc --std=c99 --pedantic -Wall -Wextra
я получаю точно такое же предупреждение о неявном объявлении.
Разве c99 не должен был объявить эту функцию неявно как возвращающую void
? Если бы это было так, я ожидал бы ошибки previous implicit declaration
, подобной той, которую я получил, когда попытался повторно объявить ее возвращением float
.
Здесь сломано gcc
, или я что-то пропустил в стандарте?
2 ответа:
Это описано в примечании к 6.5.1 основные выражения :
2 - идентификатор является первичным выражением, при условии, что он был объявлен как обозначающий объект (в этом случае это значение lvalue) или функция (в этом случае это функция обозначение). 79)
79) таким образом, необъявленный идентификатор является нарушением синтаксиса.Соответствующая реализация требуется по 5.1.1.3 Диагностика для получения диагностическое сообщение в ответ на нарушение синтаксиса выражения вызова функции, содержащего необъявленный идентификатор в качестве выражения, обозначающего вызываемую функцию. Конечно, можно свободно приступить к компиляции программы, как если бы идентификатор был объявлен в неявном стиле -
Пункт 6.5.2. 2p5 следует читать со ссылкой на ограничение 6.5.2. 2p1:int
C89.Таким образом, если выражение " , обозначающее вызываемую функцию ", не имеет типа " указатель на функцию, возвращающую тип объекта", оно должно ipso facto (по ограничению 6.5.2.2p1) иметь тип "указатель на функцию, возвращающую тип объекта ".1-выражение, обозначающее вызываемую функцию, должно иметь указатель типа на функцию возврат
void
или возврат типа объекта, отличного от типа массива.void
", и именно этот случай охватывает "в противном случае" в 6.5.2.2p5. То есть с моей вставкой в [квадрат скобки]:5 - если выражение, обозначающее вызываемую функцию, имеет указатель типа на функцию, возвращающую тип объекта, то выражение вызова функции имеет тот же тип, что и этот тип объекта, и имеет значение, определенное в соответствии с пунктом 6.8.6.4. в противном случае, [т. е. если выражение, обозначающее вызываемую функцию, имеет указатель типа на функцию, возвращающую
void
,] вызов функции имеет типvoid
.Это случай особого языкового бытия требуется для
void
в отличие от типов объектов; не является лицензией для выражения вызываемой функции, чтобы быть или содержать необъявленный идентификатор.
Вы неправильно читаете стандарт. В языке C. Нет такого понятия, как неявное объявление функции, оно удаляется из языка C99.
GCC выдает предупреждение, когда видит ошибочную конструкцию, которая выглядит как неявное объявление функции. Это нормально, насколько это касается стандарта. Стандарт требует здесь диагностики, а предупреждение-это диагностика. Вы можете использовать флаг
-Werror=implicit-function-declaration
для GCC, чтобы превратить это в ошибку.