I „m într-o clasă care folosește C, iar instructorul meu are din păcate, a folosit gets() în eșantionul de cod.

Deoarece aceasta este evident o supraveghere urâtă, care poate provoca un comportament nedefinit și alte probleme diferite (doar puțin sarcasm), am decis să pun în aplicare gets_s() , deoarece a fost un exercițiu distractiv și, uneori, nu merită să facă verificarea completă a erorilor cu fgets() și doriți doar să tăiați liniile lungi neașteptate.

Nu mă îngrijorează dacă aceasta implementează pe deplin gets_s() așa cum este specificat în standardul C11 – se presupune că acesta este doar un înlocuitor pentru gets() care nu vă depășește bufferul.

Cu toate acestea, ceea ce este foarte important este că această funcție face efectiv ceea ce face publicitate: este sigur și nu depășește memoria tampon.

Acesta este fiul meu prima dată când lucrez în C (de obicei folosesc sau ) și apreciez toate sfaturi, deși aș dori cel puțin câteva mențiuni despre siguranța acestui cod și mă interesează și portabilitatea (către compilatoarele actuale).

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; } 

și un mic fișier de testare:

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; } 

Se compilează cu succes cu gcc -Wall -Wextra -Wpedantic -Werror gets_s.c, astfel încât „sa plus.

Răspuns

Mai întâi, nu-l numi gets_s deoarece semnătura și comportamentul diferă în unele moduri subtile și nu atât de subtile, care doar duce la confuzie și frustrare. Oricum, într-adevăr nu veți dori contractul gets_s.
Numiți-l ceva descriptiv precum getline_truncated.

Știți că n <= 0 este UB în implementarea dvs.?

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

Sunt sigur că compilatorul dvs. vă avertizează despre greșeala de mai sus.
Sau nu-i cereți toate avertismentele (-Wall -Wextra -std=...)?

Vă sugerez redefinirea și redenumirea codurilor de returnare pentru a permite o mai bună testare a rezultatelor:

#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 

În acest fel puteți testa ==0 sau >=0 în funcție de ceea ce decideți că este „succes”, respectiv !=0 sau <0 pentru „eșec”.

Comentarii

  • temp = "\n" este o greșeală de greșeală numai postare; inițial am scris codul pe o mașină separată, apoi l-am retipat pentru întrebare. Sunt ' sigur că ar fi făcut parte din codul meu uld au greșit (așa cum am compilat cu -Wall -Wextra -Wpedantic -Werror.
  • @ CAD97 Acesta este motivul pentru care mulți programatori C care apreciază corectitudinea și securitatea ar fi scris '\n' == temp – în acest fel, dacă greșesc == la =, veți obține o eroare mult mai clară

Răspuns

  • Codul poate citi prea mult:

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

    Dacă temp nu este nici EOF și nici \n , personajul este pierdut. Ar fi mai bine ungetc().

  • Codul depășește mandatul. Bucla

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

    garantează că apelantul nu va vedea niciodată un șir gol. Uneori șirurile goale au semnificație semantic.

  • man fgets:

    Funcțiile fgets () și gets () nu fac distincție între sfârșitul fișierului și eroare, iar apelanții trebuie să utilizeze feof ( 3) și feror (3) pentru a determina care a avut loc.

    Sunteți într-o poziție excelentă pentru a face exact acest lucru. În loc să reveniți orbește GETS_S_ERROR, determinați ce s-a întâmplat și reveniți în consecință. De exemplu, #define GETS_S_EOF 3.

Comentarii

  • ' imit gets() prin faptul că ' citesc până la sfârșitul rândului, ideea fiind că ' este ceea ce cineva care ar fi folosit vrea. Dacă doriți comportamentul stop-at-n-minus-one-characters, folosiți doar fgets(). Sau cel puțin, acesta a fost motivul meu pentru a alege acel comportament. (Dacă doriți să spuneți că ' citesc prea mult, spuneți de ce ați alege în mod explicit acel comportament. Am ales să citesc în continuare pentru a imita gets() dacă nu ' nu a trecut de buffer-ul dvs.)
  • @ CAD97 Înțeleg că obiecția dvs. se referă la primul meu punct glonț. Codul dvs. imită într-adevăr gets și într-adevăr nu ' nu depășește memoria tampon.Ideea mea este că gets este rupt în mai multe moduri. getline mai mult sau mai puțin le rezolvă pe toate.

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *