Auswirkungen einer fehlenden Prototyp Funktion auf Funktionsaufrufe



  • Hallo liebes Forum,

    auf der Arbeit hatten wir heute ein kleines Problem. Und zwar geht es um folgendes:

    Ein Kollege hat vergessen eine Prototyp-Funktion zu deklarieren. Wodurch die main() Funktion die aufgerufene Funktion natürlich nicht kennt.
    Im Laufe des Debuggens (wir sind erst nach einer viertel Stunde darauf aufmerksam geworden, dass die Prototyp Funktion fehlt) haben wir eine interessante Beobachtung gemacht.

    Folgendes Verhalten, bezüglich der Ausgabe, ist vollkommen logisch:

    #include <stdio.h>
    
    //float rechne( float zahl2);
    
    void main(void)
    {
    	float zahl = 4;
    	printf("%f", zahl);	
    	//Ausgabe: 4
    
    	rechne(zahl);
    	scanf("zahl");
    
    }
    
    float rechne(float zahl2)
    {
    	printf("\n%f", zahl2);
    	//Ausgabe: 0
    	return zahl2;
    }
    

    Folgende Ausgabe hat uns aber im nachehin überrascht, als uns aufgefallen ist, dass der Prototyp fehlt:

    #include <stdio.h>
    
    //int rechne( int zahl2);
    
    void main(void)
    {
    	int zahl = 4;
    	printf("%d", zahl);
    	//Ausgabe: 4
    
    	rechne(zahl);
    	scanf("zahl");
    
    }
    
    int rechne(int zahl2)
    {
    	printf("\n%d", zahl2);
    	//Ausgabe: 4
    	return zahl2;
    }
    

    Warum wird in der Funktion rechne() der richtige Wert ausgegeben? Sowohl float als int sind doch primitve Datentypen? Was unterscheidet die beiden Datentypen?
    Als IDE kommt Visual Sutdio 2010 zum Einsatz.

    Vielen Dank für eure Antworten



  • p64y schrieb:

    Warum wird in der Funktion rechne() der richtige Wert ausgegeben? Sowohl float als int sind doch primitve Datentypen?

    Bei der Übergabe von Argumenten an eine Funktion, die nicht deklariert ist oder keinen Prototyp hat oder wenn das Argument auf eine Ellipse fallen würde, werden bestimmte Standard-Typumwandlungen durchgeführt. Ich krieg sie jetzt wahrscheinlich nicht im Einzelnen zusammen, aber dazu gehören u.a. die Umwandlung kleinerer Integer-Typen (wie char oder short) in int bzw., falls der Wertebereich von int nicht ausreicht, unsigned int, sowie die Umwandlung von float in double. Das ist z.B. der Grund, warum man bei printf das Zeichen %f für float und double verwenden kann, alle floats werden automatisch als double übergeben.

    Dein float wird also im ersten Beispiel als double übergeben, von der Funktion selbst jedoch wieder als float interpretiert. Vermutlich sind einfach die ersten 4 Bytes des doubles 0. Deshalb sollte dich eher das Verhalten des ersten Beispiels stutzig machen ... ach ja, das ist natürlich undefiniertes Verhalten und unbedingt zu vermeiden.



  • Der ANSI C Standard legt hier eine implizite Annahme des Compilers bei nicht (zuvor) definierten und deklarierten Funktionsaufrufen fest:

    Zunächst mal wird
    int Funktion() "angenommen".
    Dann wird noch für jeden angetroffenen Parameter ebenfalls int "angenommen".

    Unterscheidet sich die tatsächlich (später) vorgenommene Definition der Funktion mit dem zuvor verwendeten Namen von diesen "Annahmen", ist das Verhalten undefiniert.
    Der Standard legt auch nicht fest, ob und wie der Compiler hier warnen oder auch abbrechen muss.



  • Wutz schrieb:

    Der ANSI C Standard legt hier eine implizite Annahme des Compilers bei nicht (zuvor) definierten und deklarierten Funktionsaufrufen fest:

    Zunächst mal wird
    int Funktion() "angenommen".
    Dann wird noch für jeden angetroffenen Parameter ebenfalls int "angenommen".

    Wo hast du denn diesen Unfug wieder her?

    ISO/IEC 9899:1999 6.5.2.2 (6) schrieb:

    If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions. If the number of arguments does not equal the number of parameters, the behavior is undefined. If the function is defined with a type that includes a prototype, and either the prototype ends with an ellipsis (, ...) or the types of the arguments after promotion are not compatible with the types of the parameters, the behavior is undefined. If the function is defined with a type that does not include a prototype, and the types of the arguments after promotion are not compatible with those of the parameters after promotion, the behavior is undefined, except for the following cases:

    — one promoted type is a signed integer type, the other promoted type is the corresponding unsigned integer type, and the value is representable in both types;
    — both types are pointers to qualified or unqualified versions of a character type or void.

    Das erklärt dann auch, warum der Funktionsaufruf mit float fehlschlägt; der float wird per Default zu einem double umgewandelt, und dabei wird dann im ersten Maschinenwort ein Bitmuster herauskommen, das als float interpretiert 0 ergibt.

    Undefiniert ist das Verhalten aber, weil die Definition einen von int abweichenden Rückgabetyp hat. Vermutlich ist float auf der Zielplattform genau so groß wie int, aber verlassen kann man sich darauf nicht - und dann kann alles mögliche passieren.



  • Vielen Dank euch 3 für die Antworten. 🙂


Anmelden zum Antworten