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 5

2 ответа:

Это описано в примечании к 6.5.1 основные выражения :

2 - идентификатор является первичным выражением, при условии, что он был объявлен как обозначающий объект (в этом случае это значение lvalue) или функция (в этом случае это функция обозначение). 79)


79) таким образом, необъявленный идентификатор является нарушением синтаксиса.

Соответствующая реализация требуется по 5.1.1.3 Диагностика для получения диагностическое сообщение в ответ на нарушение синтаксиса выражения вызова функции, содержащего необъявленный идентификатор в качестве выражения, обозначающего вызываемую функцию. Конечно, можно свободно приступить к компиляции программы, как если бы идентификатор был объявлен в неявном стиле - int C89.

Пункт 6.5.2. 2p5 следует читать со ссылкой на ограничение 6.5.2. 2p1:

1-выражение, обозначающее вызываемую функцию, должно иметь указатель типа на функцию возврат void или возврат типа объекта, отличного от типа массива.

Таким образом, если выражение " , обозначающее вызываемую функцию ", не имеет типа " указатель на функцию, возвращающую тип объекта", оно должно ipso facto (по ограничению 6.5.2.2p1) иметь тип "указатель на функцию, возвращающую тип объекта ".void", и именно этот случай охватывает "в противном случае" в 6.5.2.2p5. То есть с моей вставкой в [квадрат скобки]:

5 - если выражение, обозначающее вызываемую функцию, имеет указатель типа на функцию, возвращающую тип объекта, то выражение вызова функции имеет тот же тип, что и этот тип объекта, и имеет значение, определенное в соответствии с пунктом 6.8.6.4. в противном случае, [т. е. если выражение, обозначающее вызываемую функцию, имеет указатель типа на функцию, возвращающую void,] вызов функции имеет тип void.

Это случай особого языкового бытия требуется для void в отличие от типов объектов; не является лицензией для выражения вызываемой функции, чтобы быть или содержать необъявленный идентификатор.

Вы неправильно читаете стандарт. В языке C. Нет такого понятия, как неявное объявление функции, оно удаляется из языка C99.

GCC выдает предупреждение, когда видит ошибочную конструкцию, которая выглядит как неявное объявление функции. Это нормально, насколько это касается стандарта. Стандарт требует здесь диагностики, а предупреждение-это диагностика. Вы можете использовать флаг -Werror=implicit-function-declaration для GCC, чтобы превратить это в ошибку.