C - scanf () vs gets () vs fgets ()
Habe ich auch ein Recht einfaches Programm für das konvertieren einer Zeichenfolge (vorausgesetzt, die zahlen eingegeben werden) in eine Ganzzahl.
Nachdem ich fertig war, bemerkte ich einige sehr eigenartige "bugs", die ich nicht beantworten kann, vor allem, weil mein beschränktes wissen darüber, wie die scanf()
gets()
und fgets()
Funktionen arbeiten. (Ich habe eine Menge Literatur zu Lesen, obwohl.)
So, ohne Schrift, zu viel text, hier ist der code des Programms:
#include <stdio.h>
#define MAX 100
int CharToInt(const char *);
int main()
{
char str[MAX];
printf(" Enter some numbers (no spaces): ");
gets(str);
// fgets(str, sizeof(str), stdin);
// scanf("%s", str);
printf(" Entered number is: %d\n", CharToInt(str));
return 0;
}
int CharToInt(const char *s)
{
int i, result, temp;
result = 0;
i = 0;
while(*(s+i) != '\0')
{
temp = *(s+i) & 15;
result = (temp + result) * 10;
i++;
}
return result / 10;
}
So, hier ist das problem, das ich habe. Erstens, bei der Verwendung von gets()
Funktion, funktioniert das Programm perfekt.
Zweite, bei der Verwendung von fgets()
das Ergebnis ist etwas falsch, weil anscheinend fgets()
Funktion liest newline (ASCII-Wert 10) Zeichen Letzte, die Schrauben bis das Ergebnis.
Dritte, bei der Verwendung von scanf()
Funktion, das Ergebnis ist völlig falsch, weil erstes Zeichen hat offenbar einen -52 ASCII-Wert. Dafür habe ich keine Erklärung.
Jetzt weiß ich, dass gets()
wird abgeraten, Sie zu verwenden, so möchte ich wissen, wenn ich fgets()
hier so nicht gelesen (oder ignoriert) newline-Zeichen.
Auch, was ist der deal mit der scanf()
Funktion in diesem Programm?
InformationsquelleAutor der Frage Marko | 2010-07-21
Du musst angemeldet sein, um einen Kommentar abzugeben.
Nie Verwendung
gets
. Es bietet keinen Schutz gegen einen buffer-overflow-Schwachstelle (das heißt, Sie können nicht sagen, wie groß der Puffer, die Sie an ihn übergeben wird, so kann es nicht verhindern, dass ein Benutzer die Eingabe einer Zeile, die größer als der Puffer und der Stress für Speicher).Vermeiden Sie die Verwendung
scanf
. Wenn nicht sorgfältig verwendet, können dieselben buffer-overflow-Probleme wiegets
. Auch ignorieren Sie, dass, es hat andere Probleme, die machen es schwer zu bedienen korrekt.In der Regel sollten Sie die Verwendung
fgets
statt, obwohl es manchmal unbequem (haben Sie zum strip den Zeilenumbruch, müssen Sie bestimmen, eine Puffer-Größe vor der Zeit, und dann müssen Sie herausfinden, was zu tun, mit Linien, die zu lang sind–halten Sie den Teil, den Sie Lesen und entsorgen Sie die überschüssigeentsorgen Sie die ganze Sache dynamisch wachsen die Puffer und versuchen Sie es erneut, etc.). Es gibt einige nicht-standard-Funktionen zur Verfügung, die dies tun, die dynamische Zuordnung für Sie (z.B.getline
auf POSIX-Systemen, Chuck Falconer ' s public domainggets
- Funktion). Beachten Sie, dassggets
hatgets
-wie Semantik, dass es Streifen ein trailing newline für Sie.InformationsquelleAutor der Antwort jamesdlin
Ja, Sie wollen vermeiden
gets
.fgets
immer Lesen Sie die neue Zeile, wenn der Puffer groß genug war, um es zu halten (was lässt Sie wissen, wenn der Puffer zu klein war und es gibt noch mehr von der Linie, die darauf warten gelesen zu werden). Wenn Sie wollen, so etwas wiefgets
das nicht gelesen wird, die neue Zeile (verlieren, Anzeige von einem zu kleinen buffer -) können Siefscanf
mit einer scan-Konvertierung wie:"%N[^\n]"
wo das " N " ersetzt wird durch die Größe des Puffers - 1.Einem leicht (wenn auch seltsame) Art zu entfernen, die nachfolgende neue-Zeile aus einem Puffer nach dem Lesen mit
fgets
ist:strtok(buffer, "\n");
Dies ist nicht, wiestrtok
verwendet werden soll, aber ich habe es auf diese Weise oft mehr, als in der vorgesehenen Art und Weise (die ich im Allgemeinen zu vermeiden).InformationsquelleAutor der Antwort Jerry Coffin
Gibt es zahlreiche Probleme mit diesem code. Wir lösen die schlecht benannte Variablen und Funktionen und untersucht die Probleme:
Zunächst
CharToInt()
sollte sein umbenannt, um die richtigeStringToInt()
da es funktioniert auf eine string nicht ein einziges Zeichen.Die Funktion
CharToInt()
[sic.] unsicher ist. Es überprüft nicht, ob die Benutzer versehentlich geht in ein NULL-Zeiger.Es nicht bestätigen-Eingang, oder mehr richtig, überspringen Ungültiger Eingabe. Wenn der Benutzer in einer nicht-Ziffer das Ergebnis enthält einen falschen Wert. also, Wenn Sie geben Sie in
N
den code*(s+i) & 15
produzieren wird 14 !?Nächsten, der unscheinbaren
temp
imCharToInt()
[sic.] genannt werden solltedigit
da, dass ist das, was es wirklich ist.Ebenfalls, die Unordnung
return result /10;
ist genau das-eine schlechte hack zu umgehen, ein buggy Umsetzung.Ebenfalls
MAX
ist schlecht genannt, weil es scheint, in Konflikt mit dem standard-Nutzung. also#define MAX(X,y) ((x)>(y))?(x):(y)
Den ausführlichen
*(s+i)
ist nicht so gut lesbar wie einfach*s
. Es gibt keine Notwendigkeit zu verwenden und Krempel in den code noch mit einem temporären indexi
.gets()
Dies ist schlecht, weil es kann überlauf der input-string-Puffer. Zum Beispiel, wenn die buffer-Größe ist 2, und geben Sie in der 16 Zeichen ist, wird überlauf
str
.scanf()
Ist das ebenso schlecht, weil es kann überlauf der input-string-Puffer.
Du erwähnst "bei der Verwendung von scanf () - Funktion, das Ergebnis ist völlig falsch, weil erstes Zeichen hat offenbar einen -52 ASCII-Wert."
Dass durch eine falsche Verwendung von scanf(). Ich war nicht in der Lage zu duplizieren dieser Fehler.
fgets()
Dies ist sicher, da Sie nicht garantieren können, die Sie nie überlaufen der input-string-Puffer, indem Sie die Puffer-Größe (die auch Platz für die NULL.)
getline()
Einige Leute haben vorgeschlagen, die C POSIX-standard
getline()
als Ersatz. Leider ist dies nicht eine praktische, tragbare Lösung, da Microsoft nicht implementieren Sie eine C-version; nur die standard-C++ string-template-Funktion wie das SO #27755191 Frage Antworten. Microsoft ' s C++getline()
verfügbar war, zumindest weit zurück, wie Visual Studio 6 aber seit der OP ist streng Fragen über C und nicht C++ ist dies nicht eine option.Misc.
Schließlich, diese Umsetzung fehlerhaft ist, es erkennt nicht die integer-überlauf. Wenn der Benutzer eine zu große Anzahl die Zahl kann negativ! also
9876543210
wird-18815698
?! Lassen Sie uns das auch fix.Dies ist leicht zu beheben ist für eine
unsigned int
. Wenn die vorherigen teilweisen Zahl ist kleiner als die aktuelle Anzahl teilweise haben wir dann übergelaufen und wir wieder das Vorherige partielle Nummer.Für eine
signed int
dies ist ein wenig mehr Arbeit. In der Montage konnten wir überprüfen das carry-flag, aber in C gibt es keine standard-built-in Möglichkeit zu erkennen, überlauf mit vorzeichenbehafteten int-math. Zum Glück, denn wir sind die Multiplikation durch eine Konstante,* 10
können wir leicht erkennen, wenn wir verwenden eine äquivalente Gleichung:Wenn x*8 überläuft dann logischerweise x*10 als gut. Für eine 32-bit-int-überlauf passieren wird, wenn x*8 = 0x100000000 somit alles, was wir tun müssen, ist zu erkennen, wenn x >= 0x20000000. Da wir nicht annehmen wollen, wie viele bits ein
int
hat, brauchen wir nur zu testen, ob die top-3-msb - (Most Significant Bit) gesetzt.Zusätzlich einen zweiten überlauf-test ist erforderlich. Wenn das msb gesetzt ist (Vorzeichen-bit) nach der Ziffer Verkettung dann wissen wir auch die Nummer übergelaufen.
Code
Hier ist eine Feste sichere version zusammen mit dem code, den Sie mit spielen können, um zu erkennen überlauf in die unsichere Versionen. Ich haben auch beide eine
signed
undunsigned
Versionen über#define SIGNED 1
InformationsquelleAutor der Antwort Michaelangel007
Richtig, dass Sie sollten verwenden Sie niemals
gets
. Wenn Sie verwenden möchtenfgets
können Sie überschreiben Sie einfach den Zeilenumbruch.Diese geht davon aus, es gibt keine eingebetteten Nullen. Eine weitere option ist die POSIX -
getline
:Den Vorteil
getline
ist es nicht Allokation und reallokation für Sie bearbeitet es möglich eingebetteten Nullen, und es gibt das zählen, so dass Sie nicht haben, um Zeit zu verschwenden mitstrlen
. Beachten Sie, dass Sie nicht verwenden können, ein array mitgetline
. Der Zeiger mußNULL
oder frei-können.Ich bin mir nicht sicher, welches Problem Sie haben mit
scanf
.InformationsquelleAutor der Antwort Matthew Flaschen
nie verwenden wird(), kann es dazu führen unprdictable überläuft. Wenn dein string-array der Größe 1000 und ich geben Sie 1001 Zeichen, kann ich buffer-overflow-Programms.
InformationsquelleAutor der Antwort Peter Miehle
Versuchen Sie es mit fgets() mit dieser modifizierten version Ihres CharToInt():
Es im wesentlichen überprüft, die Eingabe von Ziffern und ignoriert alles andere. Dies ist sehr grob, so ändern Sie es und Salz abschmecken.
InformationsquelleAutor der Antwort Amardeep AC9MF
So, ich bin nicht viel von einem Programmierer, aber lassen Sie mich versuchen, eine Antwort auf Ihre Frage über die
scanf();
. Ich denke, dass die scanf ist ziemlich gut und benutze es für praktisch alles, ohne irgendwelche Probleme. Aber Sie haben genommen, einen nicht ganz richtigen Struktur. Es sollte sein:"&Amp;" vor der Variablen ist wichtig. Es sagt das Programm, wo (in welcher variable) zum speichern des gescannten Wert.
die
fflush(stdin);
löscht den Puffer von der standard-Eingabe (Tastatur), so sind Sie weniger wahrscheinlich, um einen Pufferüberlauf.Ist und der Unterschied zwischen bekommt/scanf und fgets ist, dass
gets();
undscanf();
Scannen nur bis zum ersten Leerzeichen' '
währendfgets();
scannt die gesamte Eingabe. (aber sicher sein, zu reinigen Sie den Puffer danach, so wie Sie es sonst einen überlauf später)InformationsquelleAutor der Antwort nicolas gasser