Почему gcc разрешает передавать аргументы в функцию, определенную без аргументов?

Я не понимаю, почему этот код компилируется?

#include <stdio.h>
void foo() {
    printf("Hello\n");
}

int main() {
    const char *str = "bar";
    foo(str);
    return 0;
}

gcc даже не предупреждает о том, что передаю слишком много аргументов в foo(). Это ожидаемое поведение?

Ответ 1

В C функция, объявленная с пустым списком параметров, принимает произвольное количество аргументов при вызове, которые подчиняются обычным арифметическим акциям. Ответчик должен обеспечить, чтобы предоставленные аргументы были подходящими для определения функции.

Чтобы объявить функцию с нулевыми аргументами, вам нужно написать void foo(void);.

Это по историческим причинам; первоначально C-функции не имели прототипов, так как C эволюционировал из B, бесчисленного языка. Когда были добавлены прототипы, оригинальные декларации без текста были оставлены на языке для обратной совместимости.

Чтобы gcc предупредил о пустых списках параметров, используйте -Wstrict-prototypes:

Предупреждать, объявлена ​​или определена функция без указания типов аргументов. (Определение функции старого стиля разрешено без предупреждения, если ему предшествует объявление, которое указывает типы аргументов.)

Ответ 2

По устаревшим причинам объявление функции с () для списка параметров по существу означает "определить параметры при вызове функции". Чтобы указать, что функция не имеет параметров, используйте (void).

Редактировать: Я чувствую, что у меня репутация в этой проблеме за то, что ты старый. Просто, чтобы вы знали, что такое программирование, вот моя первая программа. (Не C, он показывает вам, с чем мы должны были работать до этого.)

Ответ 3

void foo() {
    printf("Hello\n");
}

foo(str);

в C, этот код не нарушает ограничение (он был бы, если бы он был определен в его прототипе-форме с помощью void foo(void) {/*...*/}), и, поскольку нарушения ограничений не существует, компилятор не обязан выдавать диагностику.

Но эта программа имеет поведение undefined в соответствии со следующими правилами C:

From:

(C99, 6.9.1p7) "Если декларатор содержит список типов параметров, в списке также указаны типы всех параметров: такой декларатор также служит прототипом функции для последующих вызовов одной и той же функции в том же Если декларатор включает в себя список идентификаторов, 142) типы параметров должны быть объявлены в следующем списке объявлений.

Функция foo не предоставляет прототип.

From:

(C99, 6.5.2.2p6) "Если выражение, обозначающее вызываемую функцию, имеет тип, который не содержит прототип [...] Если количество аргументов не равно числу параметров, поведение undefined".

вызов функции foo(str) - это поведение undefined.

C не требует реализации для диагностики для программы, которая вызывает поведение undefined, но ваша программа по-прежнему является ошибочной программой.

Ответ 4

Как стандарт C99 (6.7.5.3), так и стандарт C11 (6.7.6.3):

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

Поскольку объявление foo является частью определения, декларация указывает, что foo принимает 0 аргументов, поэтому вызов foo (str) находится в наименее морально неправильно. Но, как описано ниже, существуют разные степени "неправильного" в C, а компиляторы могут отличаться в том, как они работают с некоторыми видами "неправильных".

Чтобы сделать несколько более простой пример, рассмотрим следующую программу:

int f() { return 9; }
int main() {
  return f(1);
}

Если я скомпилирую вышеуказанное, используя Clang:

tmp$ cc tmp3.c
tmp3.c:4:13: warning: too many arguments in call to 'f'
  return f(1);
         ~  ^
1 warning generated.

Если я компилирую с gcc 4.8, я не получаю никаких ошибок или предупреждений, даже с -Wall. Предыдущий ответ предложил использовать -Wstrict-prototypes, который правильно сообщает, что определение f не в прототипной форме, но это действительно не так. Стандарт C допускает определение функции в форме, отличной от прототипа, такой как приведенная выше, и в Стандартах четко указано, что это определение указывает, что функция принимает 0 аргументов.

Теперь существует ограничение (C11, раздел 6.5.2.2):

Если выражение, которое обозначает вызываемую функцию, имеет тип, который включает прототип, количество аргументов должно совпадать с количеством параметров.

Однако это ограничение не применяется в этом случае, так как тип функции не включает прототип. Но вот последующий оператор в разделе семантики (а не "ограничение" ), который применяется:

Если выражение, обозначающее вызываемую функцию, имеет тип, который не включает прототип   ... Если количество аргументов не равно числу параметров, поведение undefined.

Следовательно, вызов функции приводит к поведению undefined (т.е. программа не "строго соответствует" ). Тем не менее, Стандарт требует, чтобы реализация сообщала диагностическое сообщение, когда нарушается фактическое ограничение, и в этом случае нарушение ограничения отсутствует. Следовательно, gcc не обязан сообщать об ошибке или предупреждении, чтобы быть "соответствующей реализацией".

Итак, я думаю, что ответ на вопрос, почему gcc разрешает это?, то, что gcc не обязан сообщать о чем-либо, поскольку это не является нарушением ограничения. Кроме того, gcc не требует сообщать о каждом типе поведения undefined, даже с -Wall или -Wpedantic. Это поведение undefined, что означает, что реализация может выбрать, как с этим бороться, и gcc решил скомпилировать его без предупреждений (и, по-видимому, он просто игнорирует аргумент).