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

Почему несогласованный прототип и определение с пустым списком аргументов дают разные результаты в GCC и Clang?

Предоставленный (упрощенный) фрагмент кода:

void foo(int a, int b); // declaration with prototype

int main(void)
{
    foo(1, 5); // type-checked call (i.e. because of previous prototype)
    return 0;
}

void foo() // old-style definition (with empty argument list)
{

}

и параметры командной строки (хотя, как я уже сказал, они не важны):

-x c -std=c11 -pedantic -Wall

gcc 7.2 не удается скомпилировать его со следующим сообщением об ошибке:

error: количество аргументов не соответствует прототипу

в то время как clang 4.0 переводит его без каких-либо жалоб.

Какая реализация правильна в соответствии со стандартом C? Действительно ли старое определение "отменяет" предыдущий прототип?

4b9b3361

Ответ 1

У меня нет цитаты из стандартного (отредактируйте: см. C11, глава 6.7.6.3/P14), но, как я понимаю, gcc имеет право кричать, так как вы противоречите себе.

Вы пообещали, что в определении функции в списке объявлений у вас будут два параметра типа int, но их там нет. В случае определения функции и пустой список означает, что функция не должна принимать никаких параметров. Таким образом, существует нарушение ограничений и gcc право жаловаться.

Кажется, это проблема в clang, которая, по крайней мере, не дает предупреждения.


Котировки:

Глава §6.7, P4 (Ограничения)

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

тогда, глава §6.7.6.3, P14,

Список идентификаторов объявляет только идентификаторы параметров функции. Пустое список в объявлении функции, который является частью определения этой функции, указывает, что функция не имеет параметров. Пустой список в объявлении функции, который не является частью определение этой функции указывает, что никакой информации о количестве или типах параметры поставляются.

Таким образом, это представляет собой нарушение ограничений и гарантирует, что будет испускать диагностику.

Ответ 2

(C11, 6.7p4 Constraints) "Все объявления в той же области действия, которые относятся к одному и тому же объекту или функции, должны указывать совместимые типы"

и

(C11, 6.7.6.3p14) "Список идентификаторов объявляет только идентификаторы параметров функции. Пустой список в деклараторе функции, который является частью определения этой функции, указывает, что функция не имеет параметров. [...]"

Мое мнение - ограничение 6,7p4 нарушено, и диагностика должна быть выпущена.

EDIT:

как указано @hvd, на самом деле это неверно. 6.7.6.3p14 не означает, что void foo() {} предоставляет прототип для foo согласно DR # 317. В этом смысле ограничение 6.7p4 не нарушается, и поэтому clang прав, чтобы не жаловаться.

Ответ 3

Отказ от ответственности: я не , но я играю один на .суб >

Если компилятор не выдаст диагностику, это будет несоответствие и может считаться ошибкой, если компилятор утверждает, что он соответствует. С >

C.2011 & sect; 6.7.6.3 & para; 14 (основное внимание):

Список идентификаторов объявляет только идентификаторы параметров функции. Пустое список в объявлении функции, который является частью определения этой функции, указывает, что функция не имеет параметров. Пустой список в объявлении функции, который не является частью определение этой функции указывает, что никакой информации о количестве или типах параметры поставляются.

Таким образом, определение foo не указывает никаких параметров, тогда как объявление foo ранее задало два параметра.

C.2011 & раздел; 6.7.6.3 & пункт; 15:

Для двух совместимых типов функций оба должны указывать совместимые типы возврата. 146)Более того, список типов параметров, , если оба присутствуют, соглашается в количестве параметры и использование терминатора многоточия; соответствующие параметры должны иметь совместимых типов.
146) Если оба типа функций являются старыми, типы параметров не сравниваются.

Таким образом, два декларатора foo несовместимы.
Dang! Из комментария @hvd:

Хорошо известно, что void foo() не дает прототипа даже в определении. Был DR, который ответил на это явно. Тип foo в этом определении void foo(), а не void foo(void) и void foo() и void foo(int, int) являются совместимыми типами. Этот ответ неверен.

Подчеркнутая часть текста выше из стандарта - это лазейка, которая допускает несогласие в количестве аргументов, но совместимых типов. Хотя определение функции определяет функцию, которая не принимает никаких аргументов, так как список типов параметров фактически отсутствует, на самом деле нет никакой несовместимости между типом foo в его прототипе функции и типом foo в определении функции.

Таким образом, clang 4.0 кажется правильным, поскольку нарушение ограничений отсутствует.

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


В комментариях вы фактически представили следующий пример:

void foo () {}

int main () { foo(1, 2); return 0; }

И спросил, почему компилятор не жалуется на этот случай. Это фактически адресовано здесь, но в двух словах: C.2011 по-прежнему принимает синтаксис определения функции K & R C. Таким образом, хотя void foo() {} - это определение, которое не принимает никаких аргументов, прототип, который используется для проверки аргумента, такой же, как void foo();, поскольку список пустых аргументов анализируется как K & R. Современный синтаксис C для принудительной проверки правильности будет заключаться в использовании void foo(void) {}.

Ответ 4

Из стандарта C (6.7.6.3 Объявление функций (включая прототипы))

15 Для того чтобы два типа функций были совместимыми, оба должны указывать совместимые типы возврата .146) Кроме того,... Если один тип имеет параметр тип типа, а другой тип определяется определением функции который содержит (возможно, пустой) список идентификаторов, оба соглашаются в количество параметров и тип каждого параметра прототипа должны быть совместимы с типом, который возникает в результате применения объявления по умолчанию для аргументов типа соответствующего идентификатор. (При определении совместимости типов и композитный тип, каждый параметр, объявленный с помощью функции или типа массива, взятые как имеющие скорректированный тип, и каждый параметр, объявленный с квалифицированный тип принимается за наличие безоговорочной версии его объявленный тип.)

И (6.2.7 Совместимый тип и составной тип)

2 Все объявления, относящиеся к одному объекту или функции, должны имеют совместимый тип; в противном случае поведение undefined

Таким образом, показанная программа в вопросе имеет поведение undefined. Компилятор может выдать диагностическое сообщение в качестве GCC.