2016-03-18 19 views
6

Ich habe versucht, ein Char-Array zu einem langen mit strtol richtig zu konvertieren, überprüfen Sie, ob es einen Überlauf oder Unterlauf und dann einen Int Cast auf der langen. Auf dem Weg dorthin habe ich eine Menge Code bemerkt, dass ähnliche Warum kannWarum können Sie nicht einfach prüfen, ob errno gleich ERANGE ist?

if ((result == LONG_MAX || result == LONG_MIN) && errno == ERANGE) 
{ 
    // Handle the error 
} 

sieht einfach sagen, dass Sie nicht

if(errno == ERANGE) 
{ 
    // Handle the error 
} 

Von meinem Verständnis, wenn ein Unterlauf oder Überlauf auftreten, errno gesetzt ERANGE in beiden Fällen. Ist das erstere wirklich notwendig? Kann ERANGE alleine problematisch sein?

Dies, wie mein Code sieht jetzt

char *endPtr; 
errno = 0; 
long result = strtol(str, &endPtr, 10); 

if(errno == ERANGE) 
{ 
    // Handle Error 
} 
else if(result > INT_MAX || result < INT_MIN) 
{ 
    // Handle Error 
} 
else if(endPtr == str || *endPtr != '\0') 
{ 
    // Handle Error 
} 

num = (int)result; 
return num; 

Wenn es einen Grund für die erstere ist lass es mich wissen.

+0

Es ist schwer zu sagen durch den begrenzten Kontext, den Sie uns gegeben haben. –

+0

Ich habe noch nie eine gute Erklärung dafür gefunden, warum es notwendig ist, nach 'LONG_MAX/LONG_MIN' und' ERANGE' zu suchen. Abgesehen davon, dass die Manpage das als Beispiel zeigt. Der einzige vernünftige Anwendungsfall, den ich mir vorstellen kann, ist die Unterscheidung zwischen Überlauf und Unterlauf. Ich wäre auch interessiert zu hören, ob es andere Gründe gibt. – kaylum

+0

@ kaylum Ich weiß nicht, dass mein Beispiel richtig ist, weil ich nicht unterscheiden möchte, ob ein Überlauf oder Unterlauf aufgetreten ist und dass errno in beiden Fällen auf ERANGE gesetzt ist. Wenn einer von ihnen aufgetreten ist, ist das Ergebnis ungültig. –

Antwort

5

Das erste Code-Snippet ist einfach falsch, und ich werde später erklären, warum, aber zuerst brauchen wir etwas Hintergrund.

errno ist eine thread-local Variable. Es wird auf einen Wert ungleich Null gesetzt, wenn ein Systemaufruf oder bestimmte Bibliotheksfunktionen fehlschlagen. Es bleibt unverändert, wenn ein Systemaufruf erfolgreich ist. Es enthält also immer die Fehlernummer vom letzten fehlgeschlagenen Aufruf.

Dies bedeutet, dass Sie zwei Möglichkeiten haben. Setzen Sie entweder errno vor jedem Anruf auf 0, oder verwenden Sie das Standard-Idiom für errno. Hier ist der Pseudo-Code für die Standard-Idiom

if (foo() == some_value_that_indicates_that_an_error_occurred) 
    then the value in errno applies to foo 
else 
    foo succeeded and the errno must be ignored because it could be anything 

Die meisten Programmierer die Standard-Idiom verwenden, weil Einstellung errno-0 vor jedem Systemaufruf ist ärgerlich, sich wiederholend, sich wiederholend, ärgerlich und lästig repetitiv. Ganz zu schweigen von der Tatsache, dass Sie vielleicht vergessen, errno auf 0 an der einen Stelle zu setzen, die wirklich zählt.


Zurück zum ersten Code-Snippet. Es ist falsch, weil es keinen Rückgabewert von strtol gibt, der eindeutig anzeigt, dass strtol fehlgeschlagen ist. Wenn strtolLONG_MAX zurückgibt, konnte es sein, dass ein Fehler aufgetreten ist, oder die Zeichenfolge enthielt tatsächlich die Nummer LONG_MAX. Es gibt keine Möglichkeit zu wissen, ob der strtol Aufruf erfolgreich war oder fehlgeschlagen ist. Dies bedeutet, dass das Standard-Idiom (das der erste Codeausschnitt zu implementieren versucht) nicht mit strtol verwendet werden kann.

strtol richtig nutzen zu können, müssen Sie errno-0 vor dem Aufruf setzen, wie dieser

errno = 0; 
result = strtol(buffer, &endptr, 10); 
if (errno == ERANGE) 
{ 
    // handle the error 
    // ERANGE is the only error mentioned in the C specification 
} 
else if (endptr == buffer) 
{ 
    // handle the error 
    // the conversion failed, i.e. the input string was empty, 
    // or only contained whitespace, or the first non-whitespace 
    // character was not valid 
} 

Beachten Sie, dass einige Implementierungen andere Werte ungleich Null für errno definieren. Weitere Informationen finden Sie auf der entsprechenden Manpage.

+1

Gute Antwort, aber ein Punkt: streng genommen ist 'strtol' eine Bibliotheksfunktion, kein Systemanruf. Und es ist ungewöhnlich, dass es 'errno' manipuliert. Die meisten (alle?) Systemaufrufe setzen 'errno', wenn sie fehlschlagen, aber nur wenige Bibliotheksfunktionen. –

+0

Danke für die tolle Antwort. Wenn du mich nicht fragen könntest, ist es möglich für 'errno! = 0 && result == 0'? Ich habe Code gesehen, wo sie das und 'if ((Ergebnis == LONG_MAX || Ergebnis == LONG_MIN) && errno == ERANGE)' Wie wenn Sie errno auf Null setzen, direkt bevor Sie zum Konvertieren von A123 aufrufen. Sie erwarten, dass das Ergebnis 0 ist und errno auf Null bleibt. Was könnte bewirken, dass errno in dieser Situation nicht null ist? –

+0

@SteveSummit Das stimmt, aber ich bin mir nicht sicher, wie ich das in die Antwort integrieren kann, ohne unnötige Komplexität einzuführen. Ich hoffe, es ist in Ordnung, es als Kommentar zu hinterlassen. – user3386109

2

Wenn Sie rufen

result = strtol("-2147483648", NULL, 0); 

oder

result = strtol("2147483647", NULL, 0); 

auf einem 32-Bit-Maschine, Sie gehen LONG_MIN oder LONG_MAX in result zu bekommen, auch wenn es nicht ein Fehler aufgetreten ist .

Wie Benutzer3386109 erklärt, ist eine Möglichkeit, Fehler von strtol zu erkennen, errno zuerst auf 0 zu setzen. Der andere Weg ist es, Ihnen einen Endzeiger zu geben und sich das anzusehen. Es gibt drei oder vier Fälle:

char *endptr; 
long int result = strtol(str, &endptr, 10); 
if(*str == '\0') { 
    /* str was empty */ 
} else if(endptr == str) { 
    /* str was completely invalid */ 
} else if(*endptr != '\0') { 
    /* numeric result followed by trailing nonnumeric character(s) */ 
} else { 
    /* str was a completely valid number (perhaps with leading whitespace) */ 
} 

Je nach Ihren Bedürfnissen können die ersten zwei oder drei Fälle zusammen kollabiert sein. Sie müssen sich dann sorgen (a), ob die "vollständig gültige Zahl" darstellbar war (die Sie testen können, indem Sie errno verwenden) und (b) ob "nicht-numerische Zeichen" harmlose Leerzeichen waren (was leider strtol überprüft nicht für Sie, also wenn Sie sich interessieren, müssen Sie sich überprüfen).

+0

Also ist es nicht sinnvoll, 'if (Ergebnis> INT_MAX || result

+0

@LuisAverhoff Entschuldigung, ich sagte 'INT_MAX', wenn ich' LONG_MAX' meinte. (Jetzt behoben.) Aber im Allgemeinen würde ich sagen, dass es nie einen Grund gibt, den Rückgabewert von 'strtol' explizit gegen einen minimalen oder maximalen Wert zu testen. (Der einzige Grund könnte sein, mit 'INT_MIN' und' INT_MAX' zu vergleichen, wenn Sie den Wert in einem einfachen int speichern wollten.) –

+0

Ja, ich wollte den Wert in einem einfachen int speichern. –