Funktion gibt Wert ohne return-Anweisung zurück

Lesezeit: 6 Minuten

Warum hat folgender Code eine korrekte Ausgabe? int GGT hat keine return-Anweisung, aber der Code funktioniert trotzdem? Es sind keine globalen Variablen gesetzt.

#include <stdio.h>
#include <stdlib.h>

int GGT(int, int);

void main() {
    int x1, x2;
    printf("Bitte geben Sie zwei Zahlen ein: \n");
    scanf("%d", &x1);
    scanf("%d", &x2);
    printf("GGT ist: %d\n", GGT(x1, x2));
    system("Pause");
}

int GGT(int x1, int x2) {
    while(x1 != x2) {
        if(x1 > x2) {
            /*return*/ x1 = x1 - x2;
        }
        else {
            /*return*/ x2 = x2 - x1;
        }
    }
}

  • Drehen Sie die Warnstufe an Ihrem Compiler ganz nach oben, und Sie sollten eine Meldung erhalten …

    – Oliver Charlesworth

    10. Januar 2011 um 8:30 Uhr

  • Ich erhalte eine Warnmeldung, aber mich interessiert, warum es funktioniert, setzt der Compiler einen Rückgabewert, wenn es keinen gibt?

    – Pascal Bayer

    10. Januar 2011 um 8:32 Uhr

  • ein mögliches Duplikat der C-Funktion, die als int definiert ist, aber keine return-Anweisung im Hauptteil hat, wird dennoch kompiliert

    – Ciro Santilli OurBigBook.com

    13. Mai 2015 um 7:04 Uhr

  • Beachten Sie, dass -Wreturn-type (und weniger konkret -Wall) fängt diesen Fehler ab gcc.

    – Ikegami

    17. Oktober 2019 um 3:34 Uhr


Benutzer-Avatar
ruslik

Zumindest für x86 sollte der Rückgabewert dieser Funktion in sein eax registrieren. Alles, was dort war, wird vom Aufrufer als Rückgabewert betrachtet.

Da eax als Rückgaberegister verwendet wird, wird es vom Aufgerufenen oft als “Scratch”-Register verwendet, da es nicht aufbewahrt werden muss. Dies bedeutet, dass es sehr wahrscheinlich ist, dass es als eine der lokalen Variablen verwendet wird. Da beide am Ende gleich sind, ist es wahrscheinlicher, dass der richtige Wert drin bleibt eax.

Benutzer-Avatar
kriss

Es sollte nicht funktionieren und sicherlich nicht auf allen Compilern und Zielbetriebssystemen funktionieren, selbst wenn es auf Ihrem funktioniert.

Die wahrscheinliche Erklärung ist, dass eine Funktion, die int zurückgibt, immer etwas zurückgibt, und es ist normalerweise der Inhalt eines Registers. Es kommt wahrscheinlich vor, dass das für den Rückgabewert verwendete Register in Ihrem Fall dasselbe ist, das zur Berechnung des letzten Ausdrucks vor der Rückkehr von der Funktion verwendet wird (auf x86-Zielen sicherlich eax).

Davon abgesehen darf ein optimierender Compiler, der feststellt, dass es keine Rückkehr gibt, den Code dieser Funktion vollständig entfernen. Von nun an verschwindet der Effekt, den Sie sehen (können), wenn Sie höhere Optimierungsstufen aktivieren.

Ich habe es mit gcc getestet:

gcc ohne Optimierung: gibt 10, 20 ein -> Ergebnis ist 10

gcc -O1 gibt 10, 20 ein -> Ergebnis ist 1

gcc -O2 gibt 10, 20 ein -> Ergebnis ist 0

Benutzer-Avatar
dbusch

Wenn eine Funktion so definiert ist, dass sie einen Wert zurückgibt, dies aber nicht der Fall ist, und die aufrufende Funktion versucht, den Rückgabewert zu verwenden, rufen Sie auf undefiniertes Verhalten.

Dies wird in Abschnitt 6.9.1p12 des C-Standard:

Wenn die } die eine Funktion beendet, erreicht wird und der Wert des Funktionsaufrufs vom Aufrufer verwendet wird, ist das Verhalten undefiniert.

Sie haben in diesem Fall “Glück” gehabt, dass das Programm anscheinend richtig funktioniert, aber dafür gibt es keine Garantie. Hätten Sie mit anderen Optimierungseinstellungen oder einem anderen Compiler kompiliert, könnten Sie zu anderen Ergebnissen kommen.

Die Moral von der Geschichte also: Wenn die Funktion sagt, dass sie einen Wert zurückgibt, stets einen Wert zurückgeben.

  • Ich habe eine Frage als Duplikat geschlossen, deren Antworten das EAX-Rückgaberegister usw. erklären, und die Frage zum Zusammenführen markiert …

    – Antti Haapala – Слава Україні

    17. Oktober 2019 um 4:48 Uhr


Auf x86 wird der Rückgabewert im EAX-Register gespeichert, das “zufälligerweise” auch von diesem Compiler verwendet wird, um das Ergebnis von arithmetischen Operationen (oder zumindest Subtraktionen) zu speichern. Sie können dies überprüfen, indem Sie sich die von Ihrem Compiler generierte Assembly ansehen. Ich stimme kriss zu – Sie können nicht davon ausgehen, dass dies immer der Fall sein wird, daher ist es besser, den Rückgabewert explizit anzugeben.

Offizielle Wortwahl:

6.9.1 Funktionsdefinitionen


12 Wenn die } die eine Funktion beendet, erreicht wird und der Wert des Funktionsaufrufs vom Aufrufer verwendet wird, ist das Verhalten undefiniert.

wobei “undefinierter Wohltäter” bedeutet:

1 undefiniertes Verhalten

Verhalten, bei Verwendung eines nicht portierbaren oder fehlerhaften Programmkonstrukts oder fehlerhafter Daten, für die diese Internationale Norm keine Anforderungen stellt

2 ANMERKUNG Mögliches undefiniertes Verhalten reicht vom völligen Ignorieren der Situation mit unvorhersehbaren Ergebnissen über ein für die Umgebung charakteristisches Verhalten während der Übersetzung oder Programmausführung (mit oder ohne Ausgabe einer Diagnosemeldung) bis zum Abbruch einer Übersetzung oder Ausführung (mit Ausgabe einer Diagnosemeldung).

3 BEISPIEL Ein Beispiel für undefiniertes Verhalten ist das Verhalten bei Ganzzahlüberlauf.

C 2011 Online-Entwurf

Ob Sie es glauben oder nicht, eine Funktion hat etwas anderes als eingegeben void ist nicht erforderlich Ein … Haben return Aussage. Es wird nicht durch die Sprachgrammatik erzwungen, und es gibt keine Einschränkung, dass ein Nicht-void Funktion muss a enthalten return Aussage. Die einzigen Einschränkungen sind, dass, falls vorhanden, a return Aussage in a void Funktion nicht den Wert irgendeines Ausdrucks zurückgibt, und dass a return Aussage in einer nicht-void Die Funktion muss den Wert eines Ausdrucks zurückgeben.

Warum ist das so?

C hatte zunächst kein a void Datentyp, und es gab keine Möglichkeit, eine Subroutine anzugeben, die nur wegen ihrer Nebeneffekte ausgeführt wurde und keinen Wert zurückgab. Es gab keine gute Möglichkeit, “nichts” zurückzugeben, also die return Erklärung war nicht erforderlich. C hatte zu diesem Zeitpunkt auch “implizit int” Deklarationen – Sie könnten einen Funktionskörper ohne Typ definieren, und der Compiler würde dies tun davon ausgehen es wurde getippt int:

foo( a, b )   // old style parameter declarations
  int a;      // still legal, but no longer really used
  char *b;    // for very good reasons
{
  // do something interesting with a and b
}

foo ist implizit typisiert, um zurückzukehren int, auch wenn explizit kein Wert zurückgegeben wird. Das ist in Ordnung, solange der Anrufer es nicht versucht verwenden fooder nicht vorhandene Rückgabewert von . So entwickelte sich eine Art Konvention, bei der “Prozeduren” (Funktionen, die nur für Nebeneffekte ausgeführt wurden) nicht explizit typisiert wurden und keine hatten return Aussage.

Denn Legacy-Code ist für immer das Verhalten bzgl return -Anweisungen wurde in neueren Versionen des C-Standards nicht geändert, obwohl dies implizit ist int Deklarationen wurden in C99 entfernt.

  • Ich habe eine Frage als Duplikat geschlossen, deren Antworten das EAX-Rückgaberegister usw. erklären, und die Frage zum Zusammenführen markiert.

    – Antti Haapala – Слава Україні

    17. Oktober 2019 um 4:48 Uhr


Benutzer-Avatar
ARA1307

GCC fügt in diesem Fall die Anweisung “ret” ein, während clang “ud2” einfügt und die App zur Laufzeit abstürzt.

  • Ich habe eine Frage als Duplikat geschlossen, deren Antworten das EAX-Rückgaberegister usw. erklären, und die Frage zum Zusammenführen markiert.

    – Antti Haapala – Слава Україні

    17. Oktober 2019 um 4:48 Uhr


1366240cookie-checkFunktion gibt Wert ohne return-Anweisung zurück

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy