I “m i en klasse, der bruger C, og min instruktør har desværre brugte gets() i eksempelkode.

Da dette naturligvis er et afskyeligt tilsyn, der sandsynligvis vil forårsage udefineret adfærd og andre forskellige problemer (kun lidt sarkasme), besluttede jeg at implementere gets_s() , fordi det var en sjov øvelse, og nogle gange er det bare ikke værd det for at udføre fuld fejlkontrol med fgets(), og du vil bare afkorte uventet lange linjer.

Jeg er ikke bekymret for, om dette fuldt ud implementerer gets_s() som specificeret i C11-standarden – dette skal bare være et drop-in erstatning for gets() der ikke overskrider din buffer.

Det, der er meget vigtigt, er dog, at denne funktion faktisk gør, hvad den annoncerer for: den er sikker og overskrider ikke bufferen.

Dette er min fi første gang jeg arbejder i C (jeg bruger normalt eller ), og jeg sætter pris på alt tip, selvom jeg i det mindste gerne vil nævne sikkerheden ved denne kode, og jeg er også interesseret i bærbarhed (til nuvæ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 lille 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 med succes med gcc -Wall -Wextra -Wpedantic -Werror gets_s.c, så “sa plus.

Svar

Først skal du ikke kalde det gets_s da signaturen og adfærden adskiller sig på nogle subtile og ikke så subtile måder, at bare fører til forvirring og frustration. Under alle omstændigheder vil du virkelig ikke have gets_s -kontrakten.
Kald det noget beskrivende som getline_truncated.

Ved du, at n <= 0 er UB i din implementering?

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

Jeg er sikker på, at din kompilator advarer dig om typografien ovenfor.
Eller beder du den ikke om alle advarsler (-Wall -Wextra -std=...)?

Jeg foreslår omdefinere og omdøbe dine returkoder for at give bedre resultattest:

#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åde kan du teste for ==0 eller >=0 afhængigt af hvad du beslutter er henholdsvis “succes”, !=0 eller <0 for “fiasko”.

Kommentarer

  • temp = "\n" er en tastefejl i denne kun post, jeg oprindeligt skrev koden på en separat maskine og indtastede den igen til spørgsmålet. Jeg ' er sikker på, at det havde været en del af min kode, det var ville have fejlet (som jeg kompilerede med -Wall -Wextra -Wpedantic -Werror.
  • @ CAD97 Dette er grunden til, at mange C-programmører, der sætter pris på korrekthed og sikkerhed, ville have skrevet '\n' == temp – på den måde, hvis de skriver == til =, får du en meget mere bestemt fejl

Svar

  • Koden kan læse for meget:

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

    Hvis temp hverken er EOF eller \n , tegnet er tabt. Du ville have det bedre ungetc() det.

  • Koden overskrider mandatet. Sløjfen

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

    garanterer, at den, der ringer op, aldrig vil se en tom streng. Nogle gange er tomme strenge semantisk meningsfulde.

  • man fgets:

    Funktionerne fgets () og gets () skelner ikke mellem filens slutning og fejl, og opkaldere skal bruge feof ( 3) og ferror (3) for at bestemme, hvad der opstod.

    Du er i en god position til netop det. I stedet for blindt at returnere GETS_S_ERROR, bestem hvad der skete, og vend tilbage i overensstemmelse hermed. F.eks. #define GETS_S_EOF 3.

Kommentarer

  • I ' m efterligner gets() ved at jeg ' Jeg læser til slutningen af linjen, idet ideen er, at ' er, hvad nogen, der ville have brugt ønsker. Hvis du vil have stop-at-n-minus-one-characters-opførsel, skal du bare bruge fgets(). Eller i det mindste var det min grund til at vælge den adfærd. (Hvis du vil sige, at jeg ' læser for meget, skal du sige, hvorfor du eksplicit vælger denne adfærd. Jeg valgte at fortsætte med at læse for at efterligne gets() hvis det ikke ' ikke skrev forbi din buffer.)
  • @ CAD97 Jeg forstår, at din indsigelse vedrører mit første punkt. Din kode efterligner faktisk gets, og den ' t overløber ikke bufferen.Mit punkt er, at gets er brudt på mere end en måde. getline løser dem mere eller mindre.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *