Ich verstehe nicht, warum dieser Code kompiliert wird.
#include <stdio.h>
void foo() {
printf("Hello\n");
}
int main() {
const char *str = "bar";
foo(str);
return 0;
}
gcc warnt nicht einmal davor, dass ich zu viele Argumente an foo () übergebe. Ist das erwartetes Verhalten?
c
gcc
compiler-errors
roter Drache
quelle
quelle
void foo(void)
void foo(void)
, sollte der Compiler normalerweise einen Fehler und nicht nur eine Warnung ausgeben (obwohl der offizielle Wortlaut lediglich eine "Diagnose" erfordert).Antworten:
In C akzeptiert eine mit einer leeren Parameterliste deklarierte Funktion beim Aufruf eine beliebige Anzahl von Argumenten, die den üblichen arithmetischen Heraufstufungen unterliegen. Es liegt in der Verantwortung des Aufrufers, sicherzustellen, dass die angegebenen Argumente für die Definition der Funktion geeignet sind.
Um eine Funktion mit Nullargumenten zu deklarieren, müssen Sie schreiben
void foo(void);
.Dies ist aus historischen Gründen; Ursprünglich hatten C-Funktionen keine Prototypen, da C aus B , einer typenlosen Sprache, hervorging. Beim Hinzufügen von Prototypen wurden die ursprünglichen typenlosen Deklarationen aus Gründen der Abwärtskompatibilität in der Sprache belassen.
Verwenden Sie
-Wstrict-prototypes
: gcc, um vor leeren Parameterlisten zu warnen :quelle
Aus alten Gründen bedeutet das Deklarieren einer Funktion mit
()
für eine Parameterliste im Wesentlichen „die Parameter herausfinden, wenn die Funktion aufgerufen wird“. Verwenden Sie, um anzugeben, dass eine Funktion keine Parameter hat(void)
.Bearbeiten: Ich habe das Gefühl, dass ich in diesem Problem den Ruf habe, alt zu sein. Damit Ihre Kinder wissen, wie das Programmieren früher war, ist hier mein erstes Programm . (Nicht C; es zeigt Ihnen, womit wir vorher arbeiten mussten.)
quelle
void foo() { printf("Hello\n"); } foo(str);
In C verletzt dieser Code keine Einschränkung (dies würde der Fall sein, wenn er in seiner Prototypform mit definiert wurde
void foo(void) {/*...*/}
). Da keine Einschränkung der Einschränkung vorliegt , muss der Compiler keine Diagnose ausstellen.Dieses Programm hat jedoch ein undefiniertes Verhalten gemäß den folgenden C-Regeln:
Von:
Die
foo
Funktion bietet keinen Prototyp.Von:
Der
foo(str)
Funktionsaufruf ist undefiniertes Verhalten.C schreibt der Implementierung nicht vor, eine Diagnose für ein Programm auszustellen, das undefiniertes Verhalten aufruft, aber Ihr Programm ist immer noch ein fehlerhaftes Programm.
quelle
Sowohl der C99-Standard (6.7.5.3) als auch der C11-Standard (6.7.6.3) geben Folgendes an:
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):
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:
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).
quelle
...
, aber Parameterwerte für...
Funktionen werden anders abgerufen als für "normale" Funktionen.