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 java eller kotlin ), 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
Svar
-
Koden kan læse for meget:
temp = fgetc( stdin ); if (temp == EOF || temp = "\n") return GETS_S_OK;
Hvis
temp
hverken erEOF
eller\n
, tegnet er tabt. Du ville have det bedreungetc()
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 brugefgets()
. 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 efterlignegets()
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, atgets
er brudt på mere end en måde.getline
løser dem mere eller mindre.
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
.'\n' == temp
– på den måde, hvis de skriver==
til=
, får du en meget mere bestemt fejl