Jestem w klasie, która używa języka C, a mój instruktor ma niestety użył gets() w przykładowym kodzie.

Ponieważ jest to oczywiście ohydne przeoczenie, które może powodować nieokreślone zachowanie i inne różne problemy (tylko trochę sarkazm), zdecydowałem się wdrożyć gets_s() , ponieważ było to fajne ćwiczenie i czasami po prostu nie warto aby wykonać pełne sprawdzenie błędów za pomocą fgets() i po prostu chcesz skrócić nieoczekiwanie długie wiersze.

Nie martwię się, czy to w pełni implementuje gets_s() zgodnie ze standardem C11 – ma to być zwykły zamiennik gets(), który nie” przepełnia bufora.

Jednak bardzo ważne jest to, że ta funkcja faktycznie robi to, co ogłasza: „jest bezpieczna” i nie przepełnia bufora.

To jest mój plik fi pierwszy raz pracuję w C (zwykle używam lub ) i doceniam wszystko wskazówki, chociaż chciałbym chociaż trochę wspomnieć o bezpieczeństwie tego kodu, a także interesuje mnie przenośność (do obecnych kompilatorów).

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 mały plik testowy:

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

Kompiluje się pomyślnie z gcc -Wall -Wextra -Wpedantic -Werror gets_s.c, więc to „plus.

Odpowiedź

Po pierwsze, nie nazywaj tego gets_s, ponieważ podpis i zachowanie różnią się w subtelny i niezbyt subtelny sposób. prowadzi do zamieszania i frustracji. W każdym razie naprawdę nie chciałbyś gets_s -kontraktu.
Nazwij to czymś opisowym, np. getline_truncated.

Czy wiesz, że n <= 0 jest UB w Twojej implementacji?

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

Jestem pewien, że kompilator ostrzega Cię o powyższej literówce.
Albo nie pytaj go o wszystkie ostrzeżenia (-Wall -Wextra -std=...)?

Proponuję ponowne zdefiniowanie i zmiana nazw kodów powrotu, aby umożliwić lepsze testowanie wyników:

#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 

W ten sposób możesz przetestować ==0 lub >=0 w zależności od tego, co uznasz za „sukces”, odpowiednio !=0 lub <0 dla „awarii”.

Komentarze

  • temp = "\n" to literówka w tym tylko post; pierwotnie napisałem kod na oddzielnym komputerze, a następnie przepisałem go w odpowiedzi na pytanie. ' m na pewno, jeśli byłby to część mojego kodu, to wo uld popełniło błąd (jak skompilowałem z -Wall -Wextra -Wpedantic -Werror.
  • @ CAD97 Dlatego wielu programistów C, którzy cenią poprawność i bezpieczeństwo, napisałoby '\n' == temp – w ten sposób, jeśli wpiszą literę == do =, otrzymasz znacznie bardziej określony błąd

Odpowiedź

  • Kod może czytać za dużo:

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

    Jeśli temp nie jest ani EOF, ani \n postać zostaje utracona. Lepiej ungetc() to.

  • Kod przekracza mandat. Pętla

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

    gwarantuje, że wywołujący nigdy nie zobaczy pustego ciągu. Czasami puste łańcuchy mają znaczenie semantyczne.

  • man fgets:

    Funkcje fgets () i gets () nie rozróżniają między końcem pliku a błędem, a wywołujący muszą używać feof ( 3) i ferror (3), aby określić, co się wydarzyło.

    Jesteś w doskonałej pozycji, aby to zrobić. Zamiast na ślepo zwracać GETS_S_ERROR, określ, co się stało i wróć odpowiednio. Np. #define GETS_S_EOF 3.

Komentarze

  • I ' naśladuję gets() w tym, że ' m czyta do końca wiersza, przy czym chodzi o to, że ' jest tym, co ktoś, kto użyłby chce. Jeśli chcesz zachować zachowanie stop-at-n-minus-one-character, po prostu użyj fgets(). A przynajmniej to był mój powód, dla którego wybrałem takie zachowanie. (Jeśli chcesz powiedzieć, że ' czytam za dużo, powiedz, dlaczego wybrałbyś to zachowanie wprost. Postanowiłem kontynuować czytanie, aby naśladować gets() jeśli nie ' nie zapisuje poza twoim buforem.)
  • @ CAD97 Rozumiem, że Twój sprzeciw dotyczy mojego pierwszego punktu. Twój kod rzeczywiście naśladuje gets i rzeczywiście nie ' nie przepełnia bufora.Chodzi mi o to, że gets jest uszkodzony na więcej niż jeden sposób. getline mniej więcej rozwiązuje je wszystkie.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *