Jeg er i en klasse som bruker C, og instruktøren min har dessverre brukte gets() i eksempelkode.

Ettersom dette åpenbart er en avskyelig tilsyn, som sannsynligvis vil forårsake udefinert oppførsel og andre forskjellige problemer (bare litt sarkasme), bestemte jeg meg for å implementere gets_s() , fordi det var en morsom øvelse og noen ganger er det bare ikke verdt det for å gjøre full feilkontroll med fgets() og du vil bare avkorte uventede lange linjer.

Jeg er ikke bekymret for om dette fullt ut implementerer gets_s() som spesifisert i C11-standarden – dette skal bare være en drop-in erstatning for gets() som ikke overkjørt bufferen din. P Det som er veldig viktig er imidlertid at denne funksjonen faktisk gjør det den annonserer: den er trygg og overskrider ikke bufferen.

Dette er min fi første gang du jobber i C (jeg bruker vanligvis eller ), og jeg setter pris på alle tips, selv om jeg i det minste vil nevne sikkerheten til denne koden, og er også interessert i bærbarhet (til nåværende kompilatorer).

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; } 

og en liten testfil:

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; } 

Den kompileres vellykket med gcc -Wall -Wextra -Wpedantic -Werror gets_s.c, slik at «sa pluss.

Svar

Ikke kall det gets_s da signaturen og oppførselen er forskjellig på noen subtile og ikke så subtile måter, det bare fører til forvirring og frustrasjon. Uansett vil du virkelig ikke ha gets_s -kontrakten.
Kall det noe beskrivende som getline_truncated.

Vet du at n <= 0 er UB i implementeringen din?

if (temp == EOF || temp = "\n") ^ 

Jeg er sikker på at kompilatoren din advarer deg om skrivefeilen ovenfor.
Eller ber du den ikke om alle advarsler (-Wall -Wextra -std=...)?

Jeg foreslår definere og omdøpe returkoder på nytt for å gi bedre resultattesting:

#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 

På denne måten kan du teste for ==0 eller >=0 avhengig av hva du bestemmer er «suksess», henholdsvis !=0 eller <0 for «fiasko».

Kommentarer

  • temp = "\n" er en skrivefeil i dette bare innlegg; Jeg skrev opprinnelig koden på en egen maskin og skrev den deretter inn for spørsmålet. Jeg ' er sikker på at det hadde vært en del av koden min ville ha gjort feil (som jeg kompilerte med -Wall -Wextra -Wpedantic -Werror.
  • @ CAD97 Dette er grunnen til at mange C-programmerere som verdsetter korrekthet og sikkerhet, ville skrevet '\n' == temp – på den måten, hvis de skriver == til =, får du en mye mer klar feil

Svar

  • Koden kan lese for mye:

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

    Hvis temp verken er EOF eller \n , er karakteren tapt. Du vil bedre ungetc() det.

  • Koden overskrider mandatet. Sløyfen

     do temp = fgetc( stdin ); while ( temp != EOF && temp != "\n" ); 

    garanterer at den som ringer aldri ser en tom streng. Noen ganger er tomme strenger semantisk meningsfylte.

  • man fgets:

    Funksjonene fgets () og gets () skiller ikke mellom filens slutt og feil, og innringere må bruke feof ( 3) og ferror (3) for å bestemme hva som skjedde.

    Du er i en flott posisjon til å gjøre nettopp det. I stedet for blindt å returnere GETS_S_ERROR, bestem hva som skjedde, og returner deretter. F.eks. #define GETS_S_EOF 3.

Kommentarer

  • I ' m etterligner gets() ved at jeg ' Jeg leser til slutten av linjen, idet ideen er at ' er hva noen som ville ha brukt ønsker. Hvis du vil ha stopp-ved-n-minus-ett-tegns oppførsel, er det bare å bruke fgets(). Eller i det minste, det var min grunn til å velge den oppførselen. (Hvis du vil si at jeg ' jeg leser for mye, si hvorfor du vil velge den oppførselen eksplisitt. Jeg valgte å fortsette å lese for å etterligne gets() hvis det ikke ' ikke skrev forbi bufferen din.)
  • @ CAD97 Jeg forstår at innvendingen din adresserer mitt første punkt. Koden din etterligner faktisk gets, og den oversvømmer faktisk ikke ' bufferen.Poenget mitt er at gets er ødelagt på mer enn en måte. getline løser dem mer eller mindre.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *