Ich bin in einer Klasse, die C verwendet, und mein Lehrer hat Leider hat gets()
im Beispielcode verwendet.
Da dies offensichtlich ein abscheuliches Versehen ist, kann es zu undefiniertem Verhalten und anderen verschiedenen Problemen kommen (nur ein wenig) Sarkasmus), entschied ich mich, gets_s()
zu implementieren, weil es eine lustige Übung war und manchmal einfach nicht wert ist Um eine vollständige Fehlerprüfung mit fgets()
durchzuführen, möchten Sie nur unerwartet lange Zeilen abschneiden.
Ich bin nicht besorgt, ob dies wie im C11-Standard angegeben – dies soll nur ein Ersatz für gets()
sein, der Ihren Puffer nicht überläuft. P. >
Was jedoch sehr wichtig ist, ist, dass diese Funktion tatsächlich das tut, was sie ankündigt: Sie ist sicher und überläuft den Puffer nicht.
Dies ist meine Fi Zum ersten Mal in C arbeiten (ich verwende normalerweise Java oder kotlin ), und ich schätze alles Tipps, obwohl ich zumindest die Sicherheit dieses Codes erwähnen möchte und mich auch für die Portabilität (für aktuelle Compiler) interessiere.
gets_s.h
#include <stdio.h> #include <string.h> #define GETS_S_OK 0 #define GETS_S_ERROR 1 #define GETS_S_OVERRUN 2 static inline int gets_s( char str[], int n ) { char *str_end, *fgets_return; int temp; fgets_return = fgets( str, n, stdin ); /* If fgets fails, it returns NULL. This includes the case where stdin is exhausted. */ if ( fgets_return == NULL ) { str[0] = "\0"; return GETS_S_ERROR; } str_end = str + strlen(str) - 1; if ( str_end == "\n" ) { *str_end = "\0"; return GETS_S_OK; } temp = fgetc( stdin ); if (temp == EOF || temp = "\n") return GETS_S_OK; do temp = fgetc( stdin ); while ( temp != EOF && temp != "\n" ); return GETS_S_OVERRUN; }
und eine kleine Testdatei:
gets_s.c
#include "gets_s.h" #include <stdio.h> int main() { char buffer[10]; int gets_s_return; printf("Enter up to %d characters safely.\n", sizeof(buffer) - 1); gets_s_return = gets_s( buffer, sizeof(buffer) ); printf("buffer = %s", buffer); printf("gets_s return = %d", gets_s_return); return 0; }
Es wird erfolgreich mit gcc -Wall -Wextra -Wpedantic -Werror gets_s.c
kompiliert, so dass „sa plus.
Antwort
Nennen Sie es zunächst nicht gets_s
, da sich die Signatur und das Verhalten auf subtile und weniger subtile Weise unterscheiden führt zu Verwirrung und Frustration. Wie auch immer, Sie möchten wirklich nicht, dass der gets_s
-Vertrag.
Nennen Sie ihn etwas Beschreibendes wie getline_truncated
.
Wissen Sie, dass n <= 0
UB in Ihrer Implementierung ist?
if (temp == EOF || temp = "\n") ^
Ich bin sicher, Ihr Compiler warnt Sie vor dem obigen Tippfehler.
Oder fragen Sie nicht nach allen Warnungen (-Wall -Wextra -std=...
)?
Ich schlage vor Definieren und benennen Sie Ihre Rückkehrcodes neu, um bessere Ergebnistests zu ermöglichen:
#define GETS_S_TRUNCATED 1 // Because truncation is not neccessarily an error #define GETS_S_OK 0 #define GETS_S_ERROR EOF // Because we already have an appropriate negative constant
Auf diese Weise können Sie auf oder >=0
, je nachdem, was Sie als „Erfolg“ bezeichnen, !=0
oder <0
für „Fehler“.
Kommentare
- Die
temp = "\n"
ist ein Tippfehler Ich habe den Code ursprünglich auf einem separaten Computer geschrieben und ihn dann für die Frage erneut eingegeben. ' Ich bin mir sicher, dass er Teil meines Codes war Ich hätte mich geirrt (wie ich mit-Wall -Wextra -Wpedantic -Werror
kompiliert habe. - @ CAD97 Aus diesem Grund hätten viele C-Programmierer, die Wert auf Korrektheit und Sicherheit legen, – Wenn sie auf diese Weise
==
bis=
tippen, erhalten Sie einen viel eindeutigeren Fehler
Antwort
-
Der Code lautet möglicherweise zu viel:
temp = fgetc( stdin ); if (temp == EOF || temp = "\n") return GETS_S_OK;
Wenn
temp
wederEOF
noch\n
ist ist der Charakter verloren. Sie sollten es besserungetc()
. -
Der Code überschreitet das Mandat. Die Schleife
do temp = fgetc( stdin ); while ( temp != EOF && temp != "\n" );
garantiert, dass der Aufrufer niemals eine leere Zeichenfolge sieht. Manchmal sind leere Zeichenfolgen semantisch bedeutsam.
-
man fgets
:Die Funktionen fgets () und gets () unterscheiden nicht zwischen Dateiende und Fehler, und Aufrufer müssen feof ( 3) und Fehler (3), um festzustellen, was aufgetreten ist.
Sie sind in einer hervorragenden Position, um genau das zu tun. Anstatt blind
GETS_S_ERROR
, bestimmen Sie, was passiert ist, und kehren Sie entsprechend zurück. ZB#define GETS_S_EOF 3
.
Kommentare
- Ich ' ahme
gets()
nach, indem ich ' Ich lese bis zum Ende der Zeile. Die Idee ist, dass ' jemand ist, der will. Wenn Sie das Stop-at-n-minus-one-Zeichen-Verhalten wünschen, verwenden Sie einfachfgets()
. Zumindest war das mein Grund, dieses Verhalten zu wählen. (Wenn Sie sagen möchten, dass ich ' zu viel lese, sagen Sie, warum Sie dieses Verhalten explizit auswählen würden. Ich habe mich entschieden, weiterzulesen, umgets()
wenn es nicht ' nicht über Ihren Puffer hinaus geschrieben hat.) - @ CAD97 Ich verstehe, dass Ihr Einwand meinen ersten Aufzählungspunkt betrifft. Ihr Code ahmt tatsächlich
gets
nach, und ' überläuft den Puffer tatsächlich nicht.Mein Punkt ist, dassgets
in mehr als einer Hinsicht fehlerhaft ist.getline
löst sie mehr oder weniger alle.