2015-01-18 18 views
12

Als kürzlich answering another question, ich habe ein Problem mit dem Code wie entdeckt:Erkennen integriertem Überlauf mit scanf

int n; 
scanf ("%d", &n); 

Mit strtol, können Sie Überlauf erkennen, da in diesem Fall erlaubt der Maximalwert in n eingesetzt und errno der Überlauf, um anzuzeigen, eingestellt wird, gemäß C11 7.22.1.4 The strtol, strtoll, strtoul, and strtoull functions /8:

Wenn der richtige Wert außerhalb des Bereichs der darstellbaren Werte ist, LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX, ULONG_MAX oder ULLONG_MAX i s zurückgegeben (entsprechend dem Rückgabetyp und dem Vorzeichen des Wertes, falls vorhanden), und der Wert des Makros ERANGE wird in errno gespeichert.

jedoch in den Abschnitten der Norm mit scanf tun haben, speziell C11 7.21.6.2 The fscanf function /10 sehen wir:

Wenn dieses Objekt nicht einen geeigneten Typ hat, oder wenn das Ergebnis der Konvertierung kann nicht dargestellt werden Im Objekt ist das Verhalten nicht definiert.

Nun zu mir bedeutet, dass jeder Wert zurückgegeben werden kann, und es gibt keine Erwähnung von errno auf etwas gesetzt wird. Dies kam zum Vorschein, weil der Fragesteller der verknüpften Frage 9,999,999,999 in einen 32-Bit int eingegeben und 1,410,065,407 einen Wert 233 zu klein zurückgegeben hatte, was darauf hinwies, dass er sich einfach an der Grenze des Typs herumgewickelt hatte.

Wenn ich versuchte es, bekam ich 2,147,483,647, die größte mögliche 32-Bit-Wert ohne Vorzeichen.

Also meine Frage ist wie folgt. Wie erkennen Sie bei der Verwendung der Funktionsfamilie scanf einen integrierten Überlauf? Ist es überhaupt möglich?

Jetzt soll ich, dass auf meinem System (Debian 7) erwähnen, errno-ERANGE unter diesen Umständen tatsächlich eingestellt ist, aber ich kann nichts mehr im Standard finden, die diese Mandate. Darüber hinaus ist der Rückgabewert scanf1, was auf Erfolg beim Scannen des Elements hinweist.

+0

Ich kann einen Grund sehen, warum 'errno' hier nicht gut funktioniert:' scanf' kann verschiedene Konvertierungen haben. Für welche von ihnen gilt "errno"? (Wenn 'errno' gleichbedeutend mit einem Konvertierungsfehler wäre, wäre das eindeutig, aber anscheinend ist das nicht der Fall, nicht einmal in' strtol'.) –

+0

Was ist Ihr System? Sie sagen _auf meinem System 'errno' ..._ –

+0

@iharob: Debian 7, ich werde die Frage aktualisieren, aber ich bin nicht wirklich nach einer Implementierung-spezifische Sache, eher eine, die nach dem Standard" koscher "ist. – paxdiablo

Antwort

5

Der einzige tragbare Weg besteht darin, eine Feldbreite anzugeben, z. mit "%4d" (garantiert sogar passen in eine 16-Bit int) oder durch den Aufbau der Formatzeichenfolge zur Laufzeit mit einer Feldbreite von (int)(log(INT_MAX)/log(10)). Dies weist zum Beispiel auch 32000 zurück, obwohl es in ein 16-Bit int passen würde. Also nein, es gibt keinen befriedigenden transportablen Weg.

POSIX nicht mehr hier angeben, noch ERANGE zu erwähnen.

erwähnt Einstellung errno nur in Fall EOF zurückgegeben wird; the glibc documentation erwähnt ERANGE überhaupt nicht.

Das lässt die Frage, was Anfänger für das Lesen von ganzen Zahlen vorschlagen, wo ich keine Ahnung habe.scanf hat zu viele undefinierte und unterspezifizierte Aspekte, um wirklich nützlich zu sein, fgets kann nicht im produktiven Code verwendet werden, weil Sie 0-Bytes nicht richtig behandeln können, und portable Fehlerprüfung mit strtol und Freunde nimmt mehr Zeilen als die Implementierung der Funktionalität selbst (und ist ziemlich einfach falsch verstehen). Das Verhalten von atoi ist auch für Ganzzahlüberlauf nicht definiert.