Seit ich vor vielen Jahren festgestellt habe, dass dies standardmäßig keinen Fehler erzeugt (zumindest in GCC), habe ich mich immer gefragt, warum?
Ich verstehe, dass Sie Compiler-Flags ausgeben können, um eine Warnung zu erzeugen, aber sollte es nicht immer ein Fehler sein? Warum ist es sinnvoll, dass eine nicht ungültige Funktion, die keinen Wert zurückgibt, gültig ist?
Ein Beispiel wie in den Kommentaren angefordert:
#include <stdio.h>
int stringSize()
{
}
int main()
{
char cstring[5];
printf( "the last char is: %c\n", cstring[stringSize()-1] );
return 0;
}
... kompiliert.
-Werror=return-type
behandelt nur diese Warnung als Fehler. Ich habe die Warnung einfach ignoriert und die paar Minuten der Frustration, die einen ungültigenthis
Zeiger aufspürten, führten mich hierher und zu dieser Schlussfolgerung.std::optional
Funktion ohne Rückgabe ein "wahres" optionales Ergebnis zurückgibtAntworten:
C99- und C ++ - Standards erfordern keine Funktionen, um einen Wert zurückzugeben. Die fehlende return-Anweisung in einer Funktion zur Rückgabe von Werten wird
0
nur in der definiert (um zurückzugeben )main
Funktion .Das Grundprinzip beinhaltet, dass es ziemlich schwierig ist zu überprüfen, ob jeder Codepfad einen Wert zurückgibt, und dass ein Rückgabewert mit eingebettetem Assembler oder anderen kniffligen Methoden festgelegt werden kann.
Aus C ++ 11 Entwurf:
§ 6.6.3 / 2
§ 3.6.1 / 5
Beachten Sie, dass das in C ++ 6.6.3 / 2 beschriebene Verhalten in C nicht dasselbe ist.
gcc gibt eine Warnung aus, wenn Sie es mit der Option -Wreturn-type aufrufen.
Schauen Sie sich aus Neugier an, was dieser Code bewirkt:
Dieser Code hat ein formal undefiniertes Verhalten und wird in der Praxis als konventions- und architekturabhängig bezeichnet . Auf einem bestimmten System mit einem bestimmten Compiler ist der Rückgabewert das Ergebnis der letzten Ausdrucksauswertung, die im
eax
Register des Prozessors dieses Systems gespeichert ist .quelle
throw
oder beendet wirdlongjmp
, sollte der Compilerreturn
nach dem Aufruf der nicht zurückkehrenden Funktion eine nicht erreichbare Funktion benötigen ? Der Fall, in dem es nicht benötigt wird, ist nicht sehr häufig, und eine Anforderung, es auch in solchen Fällen aufzunehmen, wäre wahrscheinlich nicht lästig gewesen, aber die Entscheidung, es nicht zu verlangen, ist vernünftig.gcc überprüft standardmäßig nicht, ob alle Codepfade einen Wert zurückgeben, da dies im Allgemeinen nicht möglich ist. Es setzt voraus, dass Sie wissen, was Sie tun. Betrachten Sie ein allgemeines Beispiel mit Aufzählungen:
Sie als Programmierer wissen, dass diese Methode, abgesehen von einem Fehler, immer eine Farbe zurückgibt. gcc vertraut darauf, dass Sie wissen, was Sie tun, sodass Sie nicht gezwungen sind, am Ende der Funktion eine Rückgabe zu setzen.
Javac hingegen versucht zu überprüfen, ob alle Codepfade einen Wert zurückgeben, und gibt einen Fehler aus, wenn nicht nachgewiesen werden kann, dass alle dies tun. Dieser Fehler wird durch die Java-Sprachspezifikation vorgeschrieben. Beachten Sie, dass es manchmal falsch ist und Sie eine unnötige return-Anweisung eingeben müssen.
Es ist ein philosophischer Unterschied. C und C ++ sind freizügigere und vertrauenswürdigere Sprachen als Java oder C #. Daher sind einige Fehler in den neueren Sprachen Warnungen in C / C ++, und einige Warnungen werden standardmäßig ignoriert oder deaktiviert.
quelle
System.exit()
niemals zurückgegeben wird.System.exit()
niemals zurückkehrt. Ich habe es nachgeschlagen ( java.sun.com/j2se/1.4.2/docs/api/java/lang/… ) und die Dokumente sagen nur, dass es "normalerweise nie zurückkehrt". Ich frage mich, was das bedeutet ...Sie meinen, warum das Ende einer Wertrückgabefunktion abfließen (dh ohne explizite Beendigung beenden)
return
) kein Fehler ist?Erstens ist in C nur dann entscheidend, ob eine Funktion etwas Sinnvolles zurückgibt oder nicht, wenn der ausführende Code tatsächlich ausgeführt wird verwendet den zurückgegebenen Wert. Vielleicht wollte die Sprache Sie nicht zwingen, etwas zurückzugeben, wenn Sie wissen, dass Sie es die meiste Zeit sowieso nicht verwenden werden.
Zweitens wollte die Sprachspezifikation die Compilerautoren anscheinend nicht zwingen, alle möglichen Steuerpfade auf das Vorhandensein eines expliziten zu erkennen und zu überprüfen
return
(obwohl dies in vielen Fällen nicht so schwierig ist). Einige Steuerpfade können auch zu nicht zurückkehrenden Funktionen führen - dem Merkmal, das dem Compiler im Allgemeinen nicht bekannt ist. Solche Pfade können zu ärgerlichen Fehlalarmen werden.Beachten Sie auch, dass sich C und C ++ in ihren Definitionen des Verhaltens in diesem Fall unterscheiden. In C ++ ist das Abfließen des Endes einer Wertrückgabefunktion immer ein undefiniertes Verhalten (unabhängig davon, ob das Ergebnis der Funktion vom aufrufenden Code verwendet wird). In C führt dies nur dann zu undefiniertem Verhalten, wenn der aufrufende Code versucht, den zurückgegebenen Wert zu verwenden.
quelle
return
Anweisungen am Ende von weglassenmain()
?main
ist in dieser Hinsicht etwas Besonderes.Unter C / C ++ ist es legal, nicht von einer Funktion zurückzukehren, die behauptet, etwas zurückzugeben. Es gibt eine Reihe von Anwendungsfällen, z. B. Anrufe
exit(-1)
oder eine Funktion, die es aufruft oder eine Ausnahme auslöst.Der Compiler wird legales C ++ nicht ablehnen, selbst wenn es zu UB führt, wenn Sie dies nicht möchten. Insbesondere bitten Sie um keine Warnungen generiert werden. (Gcc aktiviert einige standardmäßig immer noch, aber wenn sie hinzugefügt werden, scheinen diese mit neuen Funktionen übereinzustimmen, nicht mit neuen Warnungen für alte Funktionen.)
Das Ändern des Standard-No-Arg-gcc, um einige Warnungen auszugeben, kann eine wichtige Änderung für vorhandene Skripte oder Make-Systeme sein. Auch gut gestaltete
-Wall
und behandeln Warnungen oder schalten einzelne Warnungen um.Das Erlernen der Verwendung einer C ++ - Toolkette ist ein Hindernis für das Erlernen eines C ++ - Programmierers. C ++ - Toolketten werden jedoch normalerweise von und für Experten geschrieben.
quelle
Makefile
läuft es-Wall -Wpedantic -Werror
, aber dies war ein einmaliges Testskript, für das ich vergessen habe, die Argumente anzugeben.-Wduplicated-cond
Teil des-Wall
kaputten GCC-Bootstraps. Einige Warnungen, die in den meisten Codes angemessen erscheinen, sind nicht in allen Codes angemessen. Deshalb sind sie nicht standardmäßig aktiviert.int foo() { exit(-1); }
kehrt nichtint
von einer Funktion zurück, die "behauptet, int zurückzugeben". Dies ist in C ++ legal. Jetzt gibt es nichts zurück ; Das Ende dieser Funktion wird nie erreicht. Das Ende tatsächlich zu erreichen,foo
wäre undefiniertes Verhalten. Das Ignorieren von Fällen am Ende des Prozessesint foo() { throw 3.14; }
behauptet ebenfalls, zurückzukehrenint
, tut dies jedoch nie.void* foo(void* arg) { pthread_exit(NULL); }
ist aus dem gleichen Grund in Ordnung (wenn seine einzige Verwendung über istpthread_create(...,...,foo,...);
)Ich glaube, das liegt an altem Code (C hat nie eine return-Anweisung benötigt, C ++ auch nicht). Es gibt wahrscheinlich eine riesige Codebasis, die sich auf diese "Funktion" stützt. Aber zumindest gibt es
-Werror=return-type
auf vielen Compilern (einschließlich gcc und clang) eine Flagge.quelle
In einigen begrenzten und seltenen Fällen kann es nützlich sein, das Ende einer nicht leeren Funktion zu verlassen, ohne einen Wert zurückzugeben. Wie der folgende MSVC-spezifische Code:
Diese Funktion gibt pi mithilfe der x86-Assembly zurück. Im Gegensatz zur Montage in GCC kenne ich keine Verwendungsmöglichkeit
return
zu tun, ohne Overhead in das Ergebnis einzubeziehen.Soweit ich weiß, sollten Mainstream-C ++ - Compiler zumindest Warnungen für scheinbar ungültigen Code ausgeben. Wenn ich den Körper aus mache
pi()
leer mache, meldet GCC / Clang eine Warnung und MSVC meldet einen Fehler.Die Leute erwähnten Ausnahmen und
exit
in einigen Antworten. Das sind keine triftigen Gründe. Durch das Auslösen einer Ausnahme oder das Aufrufenexit
wird die Funktionsausführung nicht am Ende ausgeführt. Und die Compiler wissen es: Wenn Sie eine throw-Anweisung schreiben oderexit
den leeren Body von aufrufen,pi()
werden alle Warnungen oder Fehler eines Compilers gestoppt.quelle
void
, nachdem inline asm einen Wert im Rückgaberegister belassen hat . (x87st0
in diesem Fall EAX für Ganzzahl. Und möglicherweise xmm0 in einer aufrufenden Konvention, die float / double in xmm0 anstelle von st0 zurückgibt). Das Definieren dieses Verhaltens ist spezifisch für MSVC. Nicht einmal das Klirren-fasm-blocks
, um die gleiche Syntax zu unterstützen, macht dies sicher. Siehe Does __asm {}; den Wert von eax zurückgeben?Unter welchen Umständen erzeugt es keinen Fehler? Wenn es einen Rückgabetyp deklariert und nichts zurückgibt, klingt es für mich wie ein Fehler.
Die einzige Ausnahme, an die ich denken kann, ist die
main()
Funktion, für die überhaupt keinereturn
Anweisung erforderlich ist (zumindest in C ++; ich habe keinen der C-Standards zur Hand). Wenn es keine Rückgabe gibt, verhält es sich so, als wärereturn 0;
es die letzte Anweisung.quelle
main()
braucht einereturn
in C.return <value>;
Aussagereturn 0;
am Ende vonmain()
(undmain()
nur). Aber es istreturn 0;
trotzdem ein guter Stil, etwas hinzuzufügen .Klingt so, als müssten Sie Ihre Compiler-Warnungen aufdrehen:
quelle
return;
in einer nicht ungültigen Funktion zu verwenden, und es ist eine Einschränkungsverletzung, diereturn <value>;
in einer ungültigen Funktion verwendet wird. Aber das ist, glaube ich, nicht das Thema. Beim OP geht es, wie ich es verstanden habe, darum, eine nicht leere Funktion ohne a zu verlassenreturn
(nur die Kontrolle über das Ende der Funktion abfließen zu lassen). Es ist keine Einschränkungsverletzung, AFAIK. Der Standard sagt nur, dass es immer UB in C ++ und manchmal UB in C ist.Es ist eine Einschränkungsverletzung in c99, aber nicht in c89. Kontrast:
c89:
c99:
Selbst im
--std=c99
Modus gibt gcc nur eine Warnung aus (obwohl keine zusätzlichen-W
Flags aktiviert werden müssen, wie dies standardmäßig oder in c89 / 90 erforderlich ist).Bearbeiten Sie, um hinzuzufügen, dass in c89 "das Erreichen desjenigen, der
}
eine Funktion beendet, dem Ausführen einerreturn
Anweisung ohne Ausdruck entspricht" (3.6.6.4). In c99 ist das Verhalten jedoch undefiniert (6.9.1).quelle
return
Aussagen abdeckt . Es deckt nicht das Herunterfallen am Ende einer Funktion ab, ohne einen Wert zurückzugeben.