I ”m i en klass som använder C, och min instruktör har tyvärr använde gets()
i exempelkod.
Eftersom detta uppenbarligen är en avskyvärd tillsyn, sannolikt att orsaka odefinierat beteende och andra olika problem (bara lite sarkasm), bestämde jag mig för att implementera gets_s()
, eftersom det var en rolig övning och ibland är det bara inte värt det för att göra fullständig felkontroll med fgets()
och du vill bara avkorta oväntat långa rader.
Jag är inte orolig för om detta helt implementerar gets_s()
som specificerat i C11-standarden – detta är bara tänkt att vara en drop-in ersättning för gets()
som inte överskrider din buffert. P Det som är mycket viktigt är dock att den här funktionen faktiskt gör vad den annonserar: den är säker och överskrider inte bufferten.
Detta är min fi första gången jag arbetar i C (jag brukar använda java eller kotlin ), och jag uppskattar alla tips, även om jag åtminstone skulle vilja nämna säkerheten för den här koden, och jag är också intresserad av bärbarhet (till nuvarande 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; }
och 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 kompileras framgångsrikt med gcc -Wall -Wextra -Wpedantic -Werror gets_s.c
, så att ”sa plus.
Svar
Först ska du inte kalla det gets_s
eftersom signaturen och beteendet skiljer sig åt på vissa subtila och inte så subtila sätt, som bara leder till förvirring och frustration. Hur som helst vill du verkligen inte ha gets_s
-kontrakten.
Kalla det något beskrivande som getline_truncated
.
Vet du att n <= 0
är UB i din implementering?
if (temp == EOF || temp = "\n") ^
Jag är säker på att din kompilator varnar dig för typsnittet ovan.
Eller ber du inte om alla varningar (-Wall -Wextra -std=...
)?
Jag föreslår omdefiniera och omdefiniera dina returkoder för att möjliggöra bättre resultattester:
#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å detta sätt kan du testa för ==0
eller >=0
beroende på vad du bestämmer är ”framgång”, respektive !=0
eller <0
för ”misslyckande”.
Kommentarer
Svar
-
Koden kan läsa för mycket:
temp = fgetc( stdin ); if (temp == EOF || temp = "\n") return GETS_S_OK;
Om
temp
varkenEOF
eller\n
, karaktären är förlorad. Du ”skulle bättreungetc()
det. -
Koden överträffar mandatet. Slingan
do temp = fgetc( stdin ); while ( temp != EOF && temp != "\n" );
garanterar att den som ringer aldrig ser en tom sträng. Ibland är tomma strängar betydelsefulla.
-
man fgets
:Funktionerna fgets () och gets () skiljer inte mellan filens slut och fel, och de som ringer måste använda feof ( 3) och ferror (3) för att avgöra vilken som inträffade.
Du är i en bra position för att göra just det. Istället för att blindt återvända
GETS_S_ERROR
, bestäm vad som hände och återvänd därefter. T ex#define GETS_S_EOF 3
.
Kommentarer
- I ' m härmar
gets()
genom att jag ' jag läser till slutet av raden, idén är att ' är vad någon som skulle ha använt vill ha. Om du vill ha beteendet stop-at-n-minus-one-characters, använd barafgets()
. Eller åtminstone det var min anledning att välja det beteendet. (Om du vill säga att jag ' läser för mycket, säg varför du skulle välja det beteendet uttryckligen. Jag valde att fortsätta läsa för att härmagets()
om det inte ' inte skrev förbi din buffert.) - @ CAD97 Jag förstår att din invändning riktar mig till min första punkt. Din kod efterliknar verkligen
gets
och den ' överflödar inte bufferten.Min poäng är attgets
bryts på mer än ett sätt.getline
mer eller mindre löser dem alla.
temp = "\n"
är ett stavfel i detta bara inlägg; jag skrev ursprungligen koden på en separat maskin och skrev sedan om den för frågan. Jag ' är säker på att det hade varit en del av min kod var det skulle ha gjort fel (som jag kompilerade med-Wall -Wextra -Wpedantic -Werror
.'\n' == temp
– på det sättet, om de skriver==
till=
, får du ett mycket mer bestämt fel