Warum darf beim Aufruf einer Funktion in C nicht genügend Parameter übergeben werden?

7

Ich weiß, dass das Funktionsprototyping in C ++ obligatorisch ist, wenn die Funktion nach der main() -Funktion definiert ist, aber in C optional ist (aber empfohlen). Kürzlich habe ich ein einfaches Programm geschrieben, das zwei Zahlen hinzufügt Bei der Übergabe von Argumenten verwendete der Fehler den Punktoperator anstelle eines Kommas.

%Vor%

Wenn im obigen Programm die add(int,int) -Funktion vor der main() -Funktion definiert ist, wird das Programm sicher nicht kompilieren. Dies liegt daran, dass beim Aufruf der Funktion weniger Parameter übergeben werden als erforderlich.

Aber warum kompiliert und läuft das obige Programm gut - und gibt einen großen Müllwert als Ausgabe? Was ist der Grund? Ist es besser, das Funktionsprototyping zu verwenden, damit der Compiler den Typenkonflikt und andere Fehler beim Funktionsaufruf erkennt?

Ist das nicht definiert?

    
Destructor 28.05.2015, 16:04
quelle

3 Antworten

9

Wenn C sieht, dass Sie eine Funktion aufrufen, die noch nicht deklariert wurde, wird eine implizite Deklaration generiert, die eine beliebige Anzahl von Argumenten enthält und eine Rückgabe von int annimmt. In Ihrem Beispiel wird die implizite Deklaration von int add(); erzeugt. Das ist natürlich falsch.

Zur Laufzeit erhält die add-Funktion ein einzelnes Double auf ihrem Stack und versucht, die Bytes in einer verwirrenden und undefinierten Weise zu interpretieren.

C99 hat diese Regel geändert und verbietet das Aufrufen nicht deklarierter Funktionen, aber die meisten Compiler lassen Sie das tun, wenn auch mit einer Warnung.

Das printf ist irrelevant.

    
Ryan Haining 28.05.2015, 16:09
quelle
10

In den frühesten Tagen von C, als Methoden aussahen:

%Vor%

gegeben int x; eine Anweisung add(3,5); würde Code wie:

erzeugen %Vor%

Der Compiler müsste nichts über die Methode add nicht über den Rückgabetyp wissen (was er als int erraten könnte), um den obigen Code zu generieren. Wenn die Methode sich herausstellt, wird sie zur Zeit der Verbindung entdeckt. Code, der mehr Parameter als eine erwartete Routine lieferte, würde diese Werte auf den Stapel schieben, wo sie ignoriert würden, bis der Code sie einige Zeit später platzte.

Probleme würden auftreten, wenn der Code zu wenige Parameter, falsche Parameter oder beides übergibt. Wenn der Code weniger Parameter übergab als eine Routine erwartet hatte, hatte dies oft keine Auswirkung, wenn der aufgerufene Code nicht die späteren Parameter verwendete. Wenn code read Parameter, die nicht übergeben wurden, würden sie im Allgemeinen "Müll" -Werte lesen (obwohl es keine Garantie gibt, dass der Versuch, diese Variablen zu lesen, keine anderen Nebenwirkungen haben würde). Auf vielen Plattformen wäre der erste nicht übergebene Parameter die Rücksprungadresse, wenn auch nicht immer. Wenn Code in Parameter geschrieben wurde, die nicht übergeben wurden, würde das den Return Stack generell durcheinander bringen und zu merkwürdigem Verhalten führen.

Das Übergeben falscher Parameter führt oft dazu, dass die aufgerufene Funktion an den falschen Stellen nach ihren Argumenten sucht; Wenn die übergebenen Typen kleiner waren als erwartet, könnte dies dazu führen, dass die aufgerufene Methode auf Stack-Variablen zugreift, die ihr nicht gehören (wie im Fall der Übergabe von zu wenigen Parametern).

ANSI C standardisierte die Verwendung von Funktionsprototypen; Wenn der Compiler eine Definition oder Deklaration wie int add(int a, int b) sieht, bevor er einen Aufruf von add sieht, stellt er sicher, dass er zwei Argumente vom Typ int übergibt, auch wenn der Aufrufer etwas anderes liefert (z. B. wenn der Aufrufer übergibt) a double , wird der Wert gezwungen, vor dem Aufruf int einzugeben. Ein Versuch, zu wenige Parameter an eine Methode zu übergeben, die der Compiler gesehen hat wird zu einem Fehler führen.

Im obigen Code wird der Aufruf von add ausgeführt, bevor der Compiler eine Ahnung hat, was die Methode erwartet. Während neuere Dialekte eine solche Verwendung nicht zulassen würden, würde der Compiler standardmäßig annehmen, dass add eine Methode ist, die alle Argumente entgegennimmt, die der Aufrufer liefert und int zurückgibt. Im obigen Fall würde der aufgerufene Code wahrscheinlich erwarten, dass zwei Wörter auf den Stapel geschoben werden, aber die double -Konstante würde wahrscheinlich als zwei oder vier Wörter gedrückt werden. Somit würde die Methode wahrscheinlich für a und b zwei Wörter aus der binären Darstellung des double Wertes 15.3.

erhalten

BTW, sogar Compiler, die die angegebene Syntax akzeptieren, quieken fast immer dann, wenn eine Funktion, deren Typ implizit angenommen wurde, als etwas anderes zurückgegeben wird als int ; Viele werden auch quieken, wenn eine solche Funktion mit etwas anderem als der alten Syntax definiert wird. Während die ersten C-Compiler immer Funktionsargumente auf den Stack geschoben haben, erwarten neuere sie normalerweise in Registern. Also, wenn man

deklarieren würde %Vor%

dann würde der Code add(12.3,4.56) zwei double -Werte auf den Stack schieben und die add -Funktion aufrufen, aber wenn man stattdessen deklariert hätte:

%Vor%

Dann würde der Code add(12.3,4.56) auf vielen Systemen das Gleitkommaregister 0 mit 12.3 und das Gleitkommaregister 1 mit 4.56 vor dem Aufruf laden (ohne etwas zu schieben). Folglich sind die beiden Arten der Deklaration nicht kompatibel. Wenn Sie auf solchen Systemen versuchen, eine Methode aufzurufen, ohne sie zu deklarieren, und sie anschließend in der gleichen Kompilierungseinheit unter Verwendung der Syntax im neuen Stil deklarieren, wird ein Kompilierungsfehler verursacht. Ferner geben einige dieser Systeme den Namen von Funktionen, die Registerargumente mit zwei und nicht mit einem Unterstrich belegen, die Präfixe an, wenn die Methode mit einer neuen Stilsyntax definiert wurde, aber aufgerufen wurde, ohne dass der Compiler einen Prototyp gesehen hat würde versuchen, _add aufzurufen, obwohl die Funktion __add genannt wurde. Das würde den Linker veranlassen, das Programm abzulehnen, anstatt eine ausführbare Datei zu erzeugen, die nicht funktionieren würde.

    
supercat 28.05.2015 16:29
quelle
0
  

Warum darf beim Aufruf einer Funktion in C nicht genügend Parameter übergeben werden?

Es ist nicht.

Die Tatsache, dass Ihr Compiler nicht warnt / fehlschlägt, bedeutet nicht, dass es korrekt ist.

    
quelle