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 of ), 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

  • De 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.
  • @ CAD97 Dit is de reden waarom veel C-programmeurs die waarde hechten aan correctheid en veiligheid '\n' == temp – op die manier, als ze == naar = typen, krijg je een veel duidelijkere fout

Antwoord

  • De code leest mogelijk te veel:

     temp = fgetc( stdin ); if (temp == EOF || temp = "\n") return GETS_S_OK; 

    If temp is noch EOF noch \n , is het personage verloren. U “d beter ungetc() 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 gewoon fgets(). 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 om gets() 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 dat gets op meer dan één manier is verbroken. getline lost ze min of meer allemaal op.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *