Як стандарт 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):
Якщо вираз, що позначає викликану функцію, має тип, що включає прототип, кількість аргументів повинна узгоджуватися з кількістю параметрів.
Однак це обмеження не застосовується в цьому випадку, оскільки тип функції не включає прототип. Але ось наступне твердження в розділі семантики (не "обмеження"), яке дійсно застосовується:
Якщо вираз, що позначає викликану функцію, має тип, який не включає прототип ... Якщо кількість аргументів не дорівнює кількості параметрів, поведінка не визначена.
Отже, виклик функції справді призводить до невизначеної поведінки (тобто програма не "суворо відповідає"). Однак Стандарт вимагає лише реалізації, щоб повідомити про діагностичне повідомлення, коли фактичне обмеження порушено, і в цьому випадку порушення не існує. Отже, gcc не зобов'язаний повідомляти про помилку або попередження, щоб бути "відповідною реалізацією".
Тому я думаю, що відповідь на запитання, чому gcc це дозволяє ?, полягає в тому, що gcc не зобов’язана повідомляти про що-небудь, оскільки це не є порушенням обмежень. Більше того, gcc не претендує повідомляти про всі види невизначеної поведінки, навіть із -Wall або -Wpedantic. Це невизначена поведінка, що означає, що реалізація може вибрати, як з цим боротися, а gcc вирішила скомпілювати її без попереджень (і, очевидно, вона просто ігнорує аргумент).