Sie müssen eine andere Definition von "Fehler" für mich haben :-) Was wird die ersten beiden Male gedruckt, die Sie Ihre Funktion f
aufrufen? Ich bekomme
für meine drei Funktionsaufrufe.
Was Sie sehen, ist ein Überbleibsel aus den frühen Tagen von C, als die Leute mit ihren Funktionsaufrufen "fussballfrei" und "fancy-free" gespielt haben.
Alles, was passiert, ist, dass Sie eine Funktion f
aufrufen und drei Werte aus dem Stapel ausdrucken (ja, auch wenn Sie nur ein oder zwei geben). Was passiert, wenn Sie nicht genug bieten, ist, dass Ihr Programm wahrscheinlich nur verwenden wird, was es sowieso gab, was normalerweise zu Datenproblemen beim Lesen und einem katastrophalen Fehler beim Schreiben führt.
Das ist perfekt kompilierbar, obwohl sehr unklug, C. Und ich meine das in einem sehr realen, "undefinierten Verhalten", Sinn des Wortes (speziell bezogen auf C99: "Wenn der Ausdruck, der die aufgerufene Funktion bezeichnet, einen Typ hat das enthält keinen Prototyp, ... wenn die Anzahl der Argumente nicht der Anzahl der Parameter entspricht, ist das Verhalten nicht definiert ").
Sie sollten wirklich vollständig geformte Funktionsprototypen bereitstellen, wie zum Beispiel:
%Vor%, um sicherzustellen, dass Ihr Compiler dieses Problem aufgreift und Ellipsen ( ...
) in Variablenparameterfunktionen verwendet.
Nebenbei bemerkt, was normalerweise unter den Deckeln passiert, ist, dass die aufrufende Funktion mit einem Stapel wie folgt beginnt:
%Vor%und drückt (zum Beispiel) zwei Werte auf einen Stapel, so dass es wie folgt endet:
%Vor% Wenn die aufgerufene Funktion die ersten drei Werte auf dem Stapel verwendet (da dies der Fall ist), wird festgestellt, dass sie 1
, 2
und 11111111
hat.
Er macht, was er zu tun hat, kehrt dann zurück und die aufrufende Funktion löscht diese beiden Werte vom Stapel (dies wird als Anrufer-macht-gut-Strategie bezeichnet). Wehe jedem, der das versucht mit einer callee-made-good-Strategie :-), obwohl das in C ziemlich ungewöhnlich ist, da es variable Argumentfunktionen wie printf
ein wenig schwierig macht.
Diese Erklärung:
%Vor% ... sagt dem Compiler " f
ist eine Funktion, die eine bestimmte Anzahl von Argumenten akzeptiert und int
zurückgibt". Sie versuchen dann, es mit einem, zwei und drei Argumenten aufzurufen - C-Compiler sind konzeptuell ein Durchlauf (nach der Vorverarbeitung), so dass der Compiler zu diesem Zeitpunkt nicht über die Informationen verfügt, um mit Ihnen zu streiten.
Ihre tatsächliche Implementierung von f()
benötigt drei int
Argumente, so dass die Aufrufe, die nur ein oder zwei Argumente bereitstellen, ein nicht definiertes Verhalten auslösen - es ist ein Fehler, was bedeutet, dass der Compiler keine Fehlermeldung ausgeben muss. und alles kann passieren, wenn Sie das Programm ausführen.
%Vor%
In C deklariert dies eine Funktion, die eine variable Anzahl von Argumenten annimmt, d. h. sie ist äquivalent zu der folgenden in C ++
%Vor% Um dies zu überprüfen, verwenden Sie statt int f();
Das wird den Compiler beschweren.
Bitte beachten Sie: Hier ist auch eine C-Linker-Eigenart beteiligt ... Der C-Linker validiert nicht die Argumente, die beim Aufruf an eine Funktion übergeben werden, und verlinkt einfach auf das erste öffentliche Symbol mit demselben Namen. Daher ist die Verwendung von f () in main wegen der Deklaration von int f()
erlaubt. Aber der Linker bindet die Funktion f (int, int, int) während der Verbindungszeit an den Aufrufstellen. Hoffnung, die einen Sinn ergibt (bitte lassen Sie mich wissen, wenn es nicht tut)
Es läuft gut, da int f()
bedeutet, was andere Antwort bereits gesagt hat: es bedeutet unbestimmte Anzahl von Argumenten. Dies bedeutet, dass Sie es mit der Anzahl der gewünschten Argumente (auch mehr als 3) aufrufen können, ohne dass der Compiler etwas darüber sagt.
Der Grund, warum es "unter der Decke" funktioniert, ist, dass Argumente auf den Stapel geschoben werden und dann "von" dem Stapel in der f
-Funktion zugegriffen wird. Wenn Sie 0 Argumente übergeben, entspricht der i, j, k
der Funktion Werten auf dem Stapel, die von der Funktion PoV Müll sind. Trotzdem können Sie auf ihre Werte zugreifen. Wenn Sie 1 Argument übergeben, greift einer der drei i j k
auf den Wert zu, die anderen erhalten den Müll. Und so weiter.
Beachten Sie, dass die gleiche Argumentation funktioniert, wenn die Argumente auf andere Weise übergeben werden, aber trotzdem sind dies die verwendeten Konventionen. Ein weiterer wichtiger Aspekt dieser Konventionen ist, dass der Angerufene nicht für die Anpassung des Stapels verantwortlich ist; es ist Sache des Aufrufers, der weiß, wie viele Argumente für real gedrückt werden. Wenn es nicht so wäre, könnte die Definition von f
vorschlagen, dass es den Stapel "anpassen" muss, um drei ganze Zahlen "freizugeben", und dies würde einen Absturz irgendeiner Art verursachen.
Was Sie geschrieben haben, ist für den aktuellen Standard in Ordnung (auf gcc compiliert ohne Warnungen, sogar mit -std=c99 -pedantic
; es gibt eine Warnung, aber es geht um die fehlende int
vor der f
-Definition) obwohl viele Leute es eklig finden und das ein "veraltetes Feature" nennen. Sicherlich zeigt Ihre Verwendung im Beispielcode keine Nützlichkeit, und wahrscheinlich kann es helfen, Bugs eine verbindliche Verwendung von Prototypen zu beseitigen! (Aber immer noch, ich bevorzuge C zu Ada)
hinzufügen
Eine "nützlichere" Verwendung der "Funktion", die das Problem "undefiniertes Verhalten" nicht auslöst, könnte
sein %Vor%Wenn Sie das gleiche Programm mit dem g ++ - Compiler kompilieren, sehen Sie folgende Fehler -
%Vor%Die Verwendung von gcc mit der Option -std = c99 gibt nur eine Warnung aus
Kompilieren Sie dasselbe Programm mit demselben Standard, den g ++ standardmäßig hat, und geben Sie die folgende Meldung ein:
%Vor%Meine Antwort wäre dann, dass Compiler mit einem anderen Standard konform wären, der nicht so restriktiv ist wie der, dem c ++ entspricht.
In C muss eine Deklaration mindestens den Rückgabetyp deklarieren. Also
%Vor% deklariert eine Funktion, die den Typ int
zurückgibt. Diese Deklaration enthält keine Informationen über die Parameter, die die Funktion benötigt. Die Definition der Funktion ist
Nun ist bekannt, dass die Funktion drei int
s benötigt. Wenn Sie die Funktion mit Argumenten aufrufen, die sich von der Definition unterscheiden, erhalten Sie keinen Fehler bei der Kompilierung, sondern einen Laufzeitfehler (oder wenn Ihnen die negative Konnotation des Fehlers nicht gefällt: "undefiniertes Verhalten"). Ein C-Compiler ist nicht vom Standard zum Auffangen dieser Inkonsistenzen gezwungen.
Um diese Fehler zu vermeiden, sollten Sie geeignete Funktionsprototypen wie
verwenden %Vor%Tags und Links c