Ik “zit in een klas die C gebruikt, en mijn instructeur heeft heeft helaas gets()
gebruikt in voorbeeldcode.
Aangezien dit duidelijk een gruwelijke vergissing is, kan het ongedefinieerd gedrag en andere verschillende problemen veroorzaken (slechts een klein beetje sarcasme), besloot ik gets_s()
te implementeren, omdat het een leuke oefening was en soms het gewoon niet waard is het om een volledige foutcontrole uit te voeren met fgets()
en je wilt gewoon onverwacht lange regels afkappen.
Ik “maak me geen zorgen of dit zoals gespecificeerd in de C11-standaard – dit is alleen bedoeld als een drop-in vervanging voor gets()
die je buffer niet” overschrijdt.
Wat echter erg belangrijk is, is dat deze functie ook daadwerkelijk doet wat het adverteert: het is veilig en overschrijdt de buffer niet.
Dit is mijn fi eerste keer werken in C (ik gebruik meestal java of kotlin ), en ik waardeer alles tips, hoewel ik “op zijn minst enige vermelding van de veiligheid van deze code zou willen, en ook ben geïnteresseerd in portabiliteit (naar huidige compilers).
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; }
en een klein testbestand:
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; }
Het compileert met succes met gcc -Wall -Wextra -Wpedantic -Werror gets_s.c
, dus dat is “een pluspunt.
Antwoord
Ten eerste, noem het niet gets_s
aangezien de handtekening en het gedrag op een aantal subtiele en niet zo subtiele manieren verschillen, maar leidt tot verwarring en frustratie. Hoe dan ook, je zou echt het gets_s
-contract niet willen.
Noem het iets beschrijvends zoals getline_truncated
.
Weet u dat n <= 0
UB is in uw implementatie?
if (temp == EOF || temp = "\n") ^
Ik weet zeker dat je compiler je waarschuwt voor de typefout hierboven.
Of vraag je hem niet voor alle waarschuwingen (-Wall -Wextra -std=...
)?
Ik stel voor herdefiniëren en hernoemen van uw retourcodes om betere resultaattests mogelijk te maken:
#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
Op deze manier kunt u testen op ==0
of >=0
afhankelijk van wat u besluit “succes” is, respectievelijk !=0
of <0
voor “failure”.
Reacties
Antwoord
-
De code leest mogelijk te veel:
temp = fgetc( stdin ); if (temp == EOF || temp = "\n") return GETS_S_OK;
If
temp
is nochEOF
noch\n
, is het personage verloren. U “d beterungetc()
it. -
De code overschrijdt het mandaat. De lus
do temp = fgetc( stdin ); while ( temp != EOF && temp != "\n" );
garandeert dat de beller nooit een lege string zal zien. Soms zijn lege strings semantisch betekenisvol.
-
man fgets
:De functies fgets () en gets () maken geen onderscheid tussen end-of-file en error, en bellers moeten feof ( 3) en ferror (3) om te bepalen wat er is gebeurd.
Je bevindt je in een goede positie om precies dat te doen. In plaats van blindelings
GETS_S_ERROR
, bepaal wat er is gebeurd en dienovereenkomstig retourneren. Bijv.#define GETS_S_EOF 3
.
Opmerkingen
- Ik ' m bootst
gets()
na in dat ik ' m leest tot het einde van de regel, met de gedachte dat ' is wat iemand zou hebben gebruikt wil. Als je het gedrag van stop-bij-n-min-één-lettertekens wilt, gebruik je gewoonfgets()
. Dat was tenminste mijn reden om voor dat gedrag te kiezen. (Als je wilt zeggen dat ik ' m te veel lees, zeg dan waarom je dat gedrag expliciet zou kiezen. Ik heb ervoor gekozen door te gaan met lezen omgets()
als het ' niet voorbij je buffer heeft geschreven.) - @ CAD97 Ik begrijp dat je bezwaar mijn eerste punt betreft. Uw code bootst inderdaad
gets
na, en het loopt inderdaad niet ' over de buffer.Mijn punt is datgets
op meer dan één manier is verbroken.getline
lost ze min of meer allemaal op.
temp = "\n"
is hierin een typefout alleen post; ik heb de code oorspronkelijk op een aparte machine geschreven en daarna opnieuw getypt voor de vraag. Ik ' had zeker deel uitgemaakt van mijn code, uld hebben fouten gemaakt (zoals ik heb gecompileerd met-Wall -Wextra -Wpedantic -Werror
.'\n' == temp
– op die manier, als ze==
naar=
typen, krijg je een veel duidelijkere fout