2008-12-01 10 views
15

Gibt es einen Grund, warum Null als "Standard" -Funktion Rückgabewert verwendet wird? Ich habe festgestellt, dass mehrere Funktionen von der stdlib und fast überall sonst, wenn Sie keine richtige Zahl (z. B. pow(), strcpy()) oder einen Fehler (negative Zahlen) zurückgeben, einfach Null zurückgeben.Rationale hinter Rückgabe 0 als Standardwert in C/C++

Ich wurde gerade neugierig, nachdem ich mehrere Tests mit negierter Logik gesehen hatte. Sehr verwirrend.

Warum nicht 1, oder 0xff oder irgendeine positive Zahl für diese Angelegenheit zurückgeben?

+0

Irgendwie denke ich, das sollte in das Wiki gehen, ich liebe all diese verschiedenen Gründe, die Leute finden (und ich denke, viele von ihnen sind gültig) – kritzikratzi

+0

Ja, wahrscheinlich gibt es keine einzige richtige Antwort. Ich denke, sie sind alle plausibel und sicherlich für sich allein gültig. Ich habe sicherlich nicht das Wissen, der Richter zu sein. –

Antwort

2

Da Bash und die meisten anderen UNIX-Shell-Umgebungen 0 als Erfolg und -x als Systemfehler und x als benutzerdefinierten Fehler betrachten.

+0

Seit wann ist -x ein Systemfehlercode? Sind nicht alle Fehlercodes 0..255 auf Unix-Systemen? – strager

+0

255 könnte auch ein unsigned -127 sein :) – majelbstoat

+1

@majelbstoat, meinst du nicht -128? =] Egal; Nicht Null ist Fehler, Null ist Erfolg, auf Unix-Shells. Es ist egal, was der Wert wirklich ist, wenn es ein Fehler ist, es sei denn, es hat eine spezielle Bedeutung (was selten ist). – strager

4

Beim Shell-Scripting steht 0 für "wahr", wobei eine andere Zahl normalerweise einen Fehlercode darstellt. Die Rückgabe von 0 aus einer Hauptanwendung bedeutet, dass alles erfolgreich verlaufen ist. Dieselbe Logik kann auf den Bibliothekscode angewendet werden.

Es könnte auch einfach sein, dass sie nichts zurückgeben, die als 0 interpretiert wird (im Wesentlichen das gleiche Konzept.)

+0

Es hat wirklich nichts mit Shell-Skripten zu tun. Es hat mit der Sprache C/C++ zu tun. – carson

+0

C hat sich ziemlich auf Unix entwickelt, und Unix hat sich so ziemlich mit C entwickelt. Ich denke, deshalb hat @majelbstoat das erfunden. – strager

+0

Ich denke, er sprach über andere Funktionen als Main(). Main gibt 0 aus diesem Grund zurück, aber andere Funktionen haben nicht die gleichen Einschränkungen. –

0

Da 0 ist falsch und null in C/C++ und Sie können praktisch kurze Schnitte machen, wenn das das passiert.

1

Es gibt wahrscheinlich eine Reihe von vergessener Geschichte aus den Tagen, als alles in asm geschrieben wurde. Im Allgemeinen ist es viel einfacher, auf Null als auf andere spezifische Werte zu testen.

+0

8086: "oder ax, ax" prüft auf Null (setzt NULL-Flag, wenn ax = 0). "xor ax, ax" wird auf Null zurückgesetzt (setzt ax = 0). Jeder Opcode ist ein Byte und läuft schneller als ihre Cmp- und Mov-Gegenstücke. – strager

+1

"test eax, eax" ist ein weiterer gebräuchlicher (schneller) Befehl zum Testen auf Null –

19

Ursprünglich hatte C nicht "void". Wenn eine Funktion nichts zurückgegeben hat, haben Sie den Rückgabetyp in der Deklaration leer gelassen. Aber das bedeutete, dass es einen int zurückgab.

Also, alles hat etwas zurückgegeben, auch wenn es nichts bedeutete. Und wenn Sie keinen Rückgabewert angegeben haben, wurde der Wert, der sich in dem Register befindet, der Compiler, der zum Zurückgeben von Werten verwendet wurde, zum Rückgabewert der Funktion.

// Perfectly good K&R C code. 
NoReturn() 
{ 
    // do stuff; 
    return; 
} 

int unknownValue = NoReturn(); 

Die Leute nahmen das auf Null, um Probleme zu vermeiden.

1

Ich mag mich deswegen irren, aber ich denke, dass es hauptsächlich aus historischen Gründen ist (hysterische Rosinen?). Ich glaube, dass K & RC (Pre-ANSI) hatte keinen void-Typ, so die logische Möglichkeit, eine Funktion zu schreiben, die nichts interessantes zurückgegeben wurde, war es 0 zurück zu haben.

Sicher wird hier jemand korrigieren Ich wenn ich falsch liege ... :)

24

Das Grundprinzip ist, dass Sie die Menge aller möglichen (negativen) Rückgabewerte entsprechend verschiedenen Fehlern von die einzige Situation, in der alles in Ordnung ging zu unterscheiden. Die einfachste, prägnanteste und am weitesten verbreitete Art, eine solche Unterscheidung zu verfolgen, ist ein logischer Test, und da in C alle Zahlen bis auf Null "wahr" sind, wollen Sie Null zurückgeben, um "die einzige Situation" zu bedeuten, dh Sie wollen Null als "guter" Wert.

Die gleiche Argumentation gilt für die Rückgabewerte von Unix-Programmen, aber bei den Tests in Unix-Shell-Skripten ist die Logik umgekehrt: Ein Rückgabewert von 0 bedeutet "wahr" (zum Beispiel den Rückgabewert) von/bin/wahr).

0

Dies liegt daran, dass ein Befehl, der 0 zurückgibt, einen Erfolg anzeigt, wenn er aus einer UNIX-Shell verwendet wird.

Jeder andere Wert weist auf einen Fehler hin. Wie Paul Betts positive und negative Werte angibt, abgrenzen wo der Fehler wahrscheinlich entstanden ist, aber dies ist nur eine Konvention und kein absolutes. Eine Benutzeranwendung kann einen negativen Wert ohne eine negative Auswirkung zurückgeben (abgesehen davon, dass sie der Shell anzeigt, dass die Anwendung fehlgeschlagen ist).

1

Mein Verständnis ist, dass es mit dem Verhalten von Systemaufrufen verbunden war.

Betrachten Sie den Systemaufruf open(); Wenn es erfolgreich ist, gibt es eine nicht negative Ganzzahl zurück, bei der es sich um den erstellten Dateideskriptor handelt. Auf der Assembler-Ebene (wo es eine spezielle Nicht-C-Anweisung gibt, die in den Kernel einfängt) wird jedoch bei der Rückgabe eines Fehlers ein negativer Wert zurückgegeben. Wenn es eine Fehlerrückgabe erkennt, speichert der C-Code-Wrapper um den Systemaufruf den negierten Wert in errno (so hat errno einen positiven Wert) und die Funktion gibt -1 zurück.

Bei einigen anderen Systemaufrufen wird der negative Rückgabecode auf der Assembler-Ebene noch negiert und in errno platziert und -1 zurückgegeben. Diese Systemaufrufe haben jedoch keinen besonderen Rückgabewert, daher wurde Null zum Anzeigen des Erfolgs gewählt. Natürlich gibt es eine große Vielfalt von Systemaufrufen, aber die meisten schaffen es, diese Konventionen zu erfüllen. Zum Beispiel geben stat() und ihre Verwandten eine Struktur zurück, aber ein Zeiger auf diese Struktur wird als ein Eingabeparameter übergeben, und der Rückgabewert ist ein 0 oder -1 Status. Sogar signal() schafft es; -1 war SIG_DFL und 0 war SIG_IGN, und andere Werte waren Funktionszeiger. Es gibt einige Systemaufrufe ohne Fehlerrückgabe - getpid(), getuid() und so weiter.

Dieser Null-gibt-Erfolg-Mechanismus wurde dann von anderen Funktionen emuliert, die eigentlich keine Systemaufrufe waren.

4

Ein anderer (kleinerer) Grund hat mit Geschwindigkeit und Codegröße auf Maschinenebene zu tun.

In den meisten Prozessoren setzt jede Operation, die zu einer Null führt, automatisch das Null-Flag, und es gibt einen sehr günstigen Vorgang, um gegen das Null-Flag zu springen.

Mit anderen Worten, wenn die letzte Maschinenoperation (z. B. PUSH) uns auf Null gebracht hat, brauchen wir nur einen Sprung auf Null oder einen Sprung-nicht-Null. Wenn wir andererseits die Dinge gegen einen anderen Wert testen, müssen wir diesen Wert in das Register verschieben, eine Vergleichsoperation ausführen, die die beiden Zahlen subtrahiert, und die Gleichheit ergibt unsere Null.

+1

Auf den CPUs, die ich Assembler in (Z-80/Z-8000/8088) nur eine arithmetische Operation führte Null setzen das Flag. Eine Bewegung oder ein Stoß würde nicht. –

1

Herkömmlicherweise gibt ein Rückkehrcode von 0 an, dass Ihr Programm normal beendet wurde und alles in Ordnung ist. (Sie können sich dies als "null Fehler" merken, obwohl Sie aus technischen Gründen nicht die Anzahl der Fehler verwenden können, die Ihr Programm als Rückgabewert gefunden hat. Siehe Stil.) Ein anderer Returncode als 0 zeigt an, dass ein Fehler aufgetreten ist. Wenn der Code bei Auftreten eines Fehlers beendet wird, verwenden Sie exit und geben Sie einen Rückgabecode ungleich Null an. Source

0

Neben allen Feinheiten, die von früheren Postern gemacht wurden, räumt es auch den Code erheblich auf, wenn eine Funktion bei Erfolg 0 zurückgibt.

Betrachten:

if (somefunc()) { 
    // handle error 
} 

ist viel sauberer als:

if (!somefunc()) { 
    // handle error 
} 

oder:

if (somefunc() == somevalue) { 
    // handle error 
} 
+0

Ich war nie ein Fan davon, weil es nicht gut liest, obwohl es sauberer ist. 'if (doMyOperation())' klingt wie "wenn die Operation ausgeführt wurde", nicht "wenn die Operation fehlgeschlagen ist" – yano

0

Dieses Argument zerfällt seit: BOOL isTrue = TRUE; oder Boolean isTrue = true; isTrue = 1 Alles außer 1 gilt als FALSCH (einschließlich 0).