Sono in una classe che utilizza C e il mio insegnante ha sfortunatamente ha utilizzato gets()
nel codice di esempio.
Poiché si tratta ovviamente di una svista atroce, potrebbe causare comportamenti indefiniti e altri vari problemi (solo un po sarcasmo), ho deciso di implementare gets_s()
, perché era un esercizio divertente ea volte non valeva la pena per eseguire un controllo completo degli errori con fgets()
e vuoi solo troncare righe inaspettatamente lunghe.
Non mi interessa se questo implementa completamente gets_s()
come specificato nello standard C11 – questo dovrebbe essere solo un sostituto immediato di gets()
che non” t sovraccarica il buffer.
Tuttavia, ciò che è molto importante è che questa funzione faccia effettivamente ciò che pubblicizza: è sicura e non sovraccarica il buffer.
Questa è la mia fi la prima volta che lavoro in C (di solito uso java o kotlin ), e apprezzo tutti suggerimenti, anche se mi piacerebbe almeno un accenno alla sicurezza di questo codice e sono anche interessato alla portabilità (per i compilatori attuali).
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; }
e un piccolo file di prova:
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; }
Si compila correttamente con gcc -Wall -Wextra -Wpedantic -Werror gets_s.c
, quindi “sa plus.
Risposta
Innanzitutto, non chiamarlo gets_s
poiché la firma e il comportamento differiscono in alcuni modi sottili e non così sottili, che semplicemente porta a confusione e frustrazione. In ogni caso, davvero non vorresti “non volere il contratto gets_s
.
Chiamalo qualcosa di descrittivo come getline_truncated
.
Sai che n <= 0
è UB nella tua implementazione?
if (temp == EOF || temp = "\n") ^
Sono sicuro che il tuo compilatore ti avverta dellerrore di battitura sopra.
Oppure non lo chiedi per tutti gli avvisi (-Wall -Wextra -std=...
)?
Suggerisco ridefinire e rinominare i codici di ritorno per consentire un migliore test dei risultati:
#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
In questo modo puoi testare ==0
o >=0
a seconda di ciò che decidi è “successo”, rispettivamente !=0
o <0
per “fallimento”.
Commenti
Risposta
-
Il codice potrebbe leggere troppo:
temp = fgetc( stdin ); if (temp == EOF || temp = "\n") return GETS_S_OK;
Se
temp
non è néEOF
né\n
, il personaggio è perso. “Meglioungetc()
. -
Il codice supera il mandato. Il ciclo
do temp = fgetc( stdin ); while ( temp != EOF && temp != "\n" );
garantisce che il chiamante non vedrà mai una stringa vuota. A volte le stringhe vuote sono semanticamente significative.
-
man fgets
:Le funzioni fgets () e gets () non distinguono tra end-of-file ed error, e i chiamanti devono usare feof ( 3) e ferror (3) per determinare quale si è verificato.
Sei in unottima posizione per farlo. Invece di tornare ciecamente
GETS_S_ERROR
, determina cosa è successo e torna di conseguenza. Ad esempio#define GETS_S_EOF 3
.
Commenti
- ' imito
gets()
in quel I ' sto leggendo fino alla fine della riga, lidea è che ' è ciò che qualcuno che avrebbe usato vuole. Se desideri il comportamento stop-at-n-meno-uno-caratteri, usafgets()
. O almeno, quella era la mia ragione per scegliere quel comportamento. (Se vuoi dire che ' sto leggendo troppo, spiega perché scegli questo comportamento esplicitamente. Ho scelto di continuare a leggere per imitaregets()
se ' non ha scritto oltre il buffer.) - @ CAD97 Capisco che la tua obiezione riguarda il mio primo punto elenco. Il tuo codice in effetti imita
gets
e in effetti non ' supera il buffer.Il punto è chegets
non funziona in più di un modo.getline
più o meno li risolve tutti.
temp = "\n"
è un errore di battitura in questo post only; Inizialmente ho scritto il codice su una macchina separata, quindi lho ridigitato per la domanda. ' sono sicuro che fosse stato parte del mio codice wo potrei aver sbagliato (come ho compilato con-Wall -Wextra -Wpedantic -Werror
.'\n' == temp
– in questo modo, se digitano==
in=
, ottieni un errore molto più preciso