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 o ), 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

  • 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.
  • @ CAD97 Questo è il motivo per cui molti programmatori C che apprezzano la correttezza e la sicurezza avrebbero scritto '\n' == temp – in questo modo, se digitano == in =, ottieni un errore molto più preciso

Risposta

  • Il codice potrebbe leggere troppo:

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

    Se temp non è né EOF\n , il personaggio è perso. “Meglio ungetc().

  • 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, usa fgets(). 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 imitare gets() 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 è che gets non funziona in più di un modo. getline più o meno li risolve tutti.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *