Sowohl der C99-Standard (6.7.5.3) als auch der C11-Standard (6.7.6.3) geben Folgendes an:
Eine Bezeichnerliste deklariert nur die Bezeichner der Parameter der Funktion. Eine leere Liste in einem Funktionsdeklarator, die Teil einer Definition dieser Funktion ist, gibt an, dass die Funktion keine Parameter hat. Die leere Liste in einem Funktionsdeklarator, die nicht Teil einer Definition dieser Funktion ist, gibt an, dass keine Informationen über die Anzahl oder den Typ der Parameter angegeben werden.
Da die Deklaration von foo Teil einer Definition ist, gibt die Deklaration an, dass foo 0 Argumente akzeptiert, sodass der Aufruf foo (str) zumindest moralisch falsch ist. Wie unten beschrieben, gibt es in C unterschiedliche Grade von "falsch", und Compiler können sich darin unterscheiden, wie sie mit bestimmten Arten von "falsch" umgehen.
Um ein etwas einfacheres Beispiel zu nennen, betrachten Sie das folgende Programm:
int f() { return 9; }
int main() {
return f(1);
}
Wenn ich das oben genannte mit Clang kompiliere:
tmp$ cc tmp3.c
tmp3.c:4:13: warning: too many arguments in call to 'f'
return f(1);
~ ^
1 warning generated.
Wenn ich mit gcc 4.8 kompiliere, erhalte ich auch mit -Wall keine Fehler oder Warnungen. In einer früheren Antwort wurde die Verwendung von -Wstrict-Prototypen vorgeschlagen, die korrekt angeben, dass die Definition von f nicht in Prototypform vorliegt, aber dies ist wirklich nicht der Punkt. Die C-Standards erlauben eine Funktionsdefinition in einer Nicht-Prototyp-Form wie der oben genannten, und die Standards geben eindeutig an, dass diese Definition angibt, dass die Funktion 0 Argumente akzeptiert.
Jetzt gibt es eine Einschränkung (C11, Abschnitt 6.5.2.2):
Wenn der Ausdruck, der die aufgerufene Funktion bezeichnet, einen Typ hat, der einen Prototyp enthält, muss die Anzahl der Argumente mit der Anzahl der Parameter übereinstimmen.
Diese Einschränkung gilt jedoch in diesem Fall nicht, da der Funktionstyp keinen Prototyp enthält. Aber hier ist eine nachfolgende Aussage im Abschnitt Semantik (keine "Einschränkung"), die gilt:
Wenn der Ausdruck, der die aufgerufene Funktion bezeichnet, einen Typ hat, der keinen Prototyp enthält ... Wenn die Anzahl der Argumente nicht der Anzahl der Parameter entspricht, ist das Verhalten undefiniert.
Daher führt der Funktionsaufruf zu undefiniertem Verhalten (dh das Programm ist nicht "streng konform"). Der Standard erfordert jedoch nur dann eine Implementierung, um eine Diagnosemeldung zu melden, wenn eine tatsächliche Einschränkung verletzt wird. In diesem Fall liegt keine Verletzung einer Einschränkung vor. Daher muss gcc keinen Fehler oder keine Warnung melden, um eine "konforme Implementierung" zu sein.
Daher denke ich, dass die Antwort auf die Frage, warum gcc dies zulässt, darin besteht, dass gcc nicht verpflichtet ist, etwas zu melden, da dies keine Einschränkung darstellt. Darüber hinaus behauptet gcc nicht, jede Art von undefiniertem Verhalten zu melden, selbst bei -Wall oder -Wpedantic. Es ist ein undefiniertes Verhalten, was bedeutet, dass die Implementierung wählen kann, wie damit umgegangen werden soll, und gcc hat sich entschieden, es ohne Warnungen zu kompilieren (und anscheinend ignoriert es das Argument einfach).