Die Deklaration der Funktion fgets
sieht folgendermaßen aus:
Dies bedeutet, dass das zweite Argument voraussichtlich ein int
ist.
Was ist der richtige Weg, um dieses Casting im folgenden Programm zu vermeiden?
%Vor% Hier habe ich (int)len
verwendet, was gut aussieht, aber was passiert, wenn buffer
eine sehr lange Zeichenfolge speichert?
Sagen wir:
%Vor% Ich habe bereits die Länge des Typs size_t
angegeben, was hier in Ordnung ist, aber wenn ich sie an% co_de weitergeben werde, ist% nicht OK wegen:
und wenn ich es auf fgets
umsetze passt nicht, weil die Größe von int
kleiner ist als die Größe von int
und einige Informationen verloren gehen.
Vielleicht fehlt mir hier etwas ...
Wie kann ich diese Situation vermeiden?
Mit fgets()
kann der Eingabepuffer s[INT_MAX]
und darüber hinaus nicht verwendet werden.
Der size_t len
-Code von OP könnte die int
-Cast von len
vermeiden, indem er in eine Zeichenkette konvertiert und wieder in eine int
konvertiert, was einfach verschwenderisch ist. Eine Besetzung ist das Richtige.
Anstatt Code mit (int)
zu streichen, reduzieren / kontrollieren Sie dessen Verwendung und fassen Sie den Cast in eine einschränkende Hilfsfunktion.
Wenn der Code wirklich lange Zeilen lesen muss, berücksichtigen Sie ssize_t getline(char **lineptr, size_t *n, FILE *stream);
wie definiert hier . Beachten Sie, dass dies eine nicht standardmäßige C-Bibliotheksfunktion ist, deren Quellcode jedoch leicht verfügbar ist.
Bezüglich der pedantischen Verwendung von fgets()
gibt es mindestens 2 Gründe dafür, dass fgets()
NULL
zurückgibt: Dateiende- und Eingabefehler. Betrachten Sie nun den Eckfall mit dem OP-Code
und pathologische Fälle wie
%Vor%Beide werden unter diskutiert. Gibt fgets () NULL mit einem kurzen Buffer-kompatiblen Befehl zurück?
Wenn Sie das Problem des Integer-Überlaufs vermeiden möchten, können Sie eine Wertüberprüfung durchführen und entsprechend handeln. Beachten Sie, dass, da strlen()
den Wert vom Typ size_t
zurückgibt, Sie 2 Optionen haben. Deklarieren Sie entweder eine Variable vom Typ int
und weisen Sie ihr den Rückgabewert strlen()
zu, der eine implizite Konvertierung vom Typ size_t
nach int
durchführt oder wie in Ihrem Funktionsaufruf umgesetzt wird.
Hier ist eine mögliche Lösung:
%Vor% Ich würde nicht die Dimension des Arguments mit fgets()
mit der Dimension von buffer
verknüpfen.
Stattdessen würde ich sicherstellen, dass der zum Lesen verwendete Puffer eine Länge hat, die mit int
dargestellt werden kann (dh INT_MAX nicht überschreitet). Wenn Sie wirklich portabel sein möchten, stellen Sie sicher, dass die Pufferlänge 32767
nicht überschreitet (der Standard gibt an, dass der minimal zulässige Wert von INT_MAX
32767
ist).
Dann nutzen Sie die Tatsache, dass fgets()
eine Zeile in Teilen liest, wenn die Zeilenlänge die Pufferlänge überschreitet.
Angenommen, len
überschreitet die Länge einer Zeile, die von stdin
gelesen wird;
Beachten Sie, dass, wenn dem Dateiende nicht unmittelbar ein '\n'
vorangestellt wird, der obige Text den Text nach dem letzten '\n'
verwirft.
Im obigen Fall ist der Ersatz von fgets(read_buffer, 10, stdin)
durch fgets(read_buffer, sizeof read_buffer, stdin)
sicher, da ein size_t
mit einem Wert kleiner oder gleich INT_MAX
immer sicher in int
konvertiert werden kann. Wenn Sie also einen Compiler von der Ausgabe von Warnungen abhalten möchten, können Sie sicher casten, d. H.% Co_de%
Entwurf sagt, dass
Das Ergebnis der Umwandlung einer Ganzzahl in eine kürzere Ganzzahl mit Vorzeichen oder das Ergebnis der Umwandlung einer vorzeichenlosen Ganzzahl in eine vorzeichenbehaftete Ganzzahl von gleiche Länge, wenn der Wert nicht dargestellt werden kann
ist implementierungsdefiniert. Als Ergebnis, wenn Sie den Wert eines vorzeichenlosen Typs sicher in ein Vorzeichen konvertieren möchten, müssen Sie sicherstellen, dass alle möglichen Quellwerte mit dem Zieltyp signed dargestellt werden können.