Почему 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 решил скомпилировать его без предупреждений (и, по-видимому, он просто игнорирует аргумент).