Jsem ve třídě, která používá C, a můj instruktor má bohužel použit gets() v ukázkovém kódu.

Jelikož je to zjevně ohavný dohled, který pravděpodobně způsobí nedefinované chování a další různé problémy (jen málo sarcasm), rozhodl jsem se implementovat gets_s() , protože to bylo zábavné cvičení a někdy to prostě nestojí za provede úplnou kontrolu chyb pomocí fgets() a vy jen chcete zkrátit neočekávaně dlouhé řádky.

Nezajímá mě, zda to plně implementuje gets_s() jak je specifikováno ve standardu C11 – toto má být náhradou za gets(), která nepřekročí váš buffer.

Co je však velmi důležité, je, že tato funkce skutečně dělá to, co inzeruje: je bezpečná a nepřekročí vyrovnávací paměť.

Toto je moje fi první čas v C (obvykle používám nebo ) a vážím si všech tipy, i když bych rád alespoň zmínil bezpečnost tohoto kódu a zajímám se také o přenositelnost (k současným překladačům).

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

a malý testovací soubor:

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

Úspěšně se kompiluje s gcc -Wall -Wextra -Wpedantic -Werror gets_s.c, takže „sa plus.

Odpovědět

Nejdříve to nenazývejte gets_s, protože podpis a chování se liší jemnými a ne tak jemnými způsoby, že jen vede ke zmatku a frustraci. Opravdu byste nechtěli gets_s smlouvu.
Nazvěte to něčím popisným jako getline_truncated.

Víte, že n <= 0 je ve vaší implementaci UB?

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

Jsem si jistý, že vás váš kompilátor varuje před překlepem výše.
Nebo od něj nežádáte všechna varování (-Wall -Wextra -std=...)?

Navrhuji nové definování a přejmenování návratových kódů, aby se umožnilo lepší testování výsledků:

#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 

Tímto způsobem můžete otestovat ==0 nebo >=0 podle toho, co rozhodnete jako „úspěch“, !=0 nebo <0 pro „selhání“.

Komentáře

  • temp = "\n" je překlep v tomto pouze příspěvek; Původně jsem kód napsal na samostatném stroji a poté jsem jej na otázku přepsal. Jsem si ' jistý, že byl součástí mého kódu, ale Uld have errored (as I compiled with -Wall -Wextra -Wpedantic -Werror.
  • @ CAD97 To je důvod, proč mnoho programátorů jazyka C, kteří oceňují správnost a bezpečnost, by napsalo '\n' == temp – tímto způsobem, pokud překlepnou == do =, dostanete mnohem jednoznačnější chybu

Odpověď

  • Kód může přečíst příliš mnoho:

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

    Pokud temp není ani EOF ani \n , postava je ztracena. Lepší je ungetc() to.

  • Kód překračuje mandát. Smyčka

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

    zaručuje, že volající nikdy neuvidí prázdný řetězec. Někdy jsou prázdné řetězce sémanticky smysluplné.

  • man fgets:

    Funkce fgets () a gets () nerozlišují mezi koncem souboru a chybou a volající musí použít feof ( 3) a ferror (3) k určení, k čemu došlo.

    Jste ve skvělé pozici, abyste to dokázali. Místo slepého návratu GETS_S_ERROR, zjistit, co se stalo, a podle toho se vrátit. Např. #define GETS_S_EOF 3.

Komentáře

  • I ' m napodobování gets() tím, že I ' m čtení na konec řádku, myšlenka spočívá v tom, že ' je to, co někdo, kdo by použil chce. Pokud chcete chování stop-at-n-minus-one-characters, stačí použít fgets(). Nebo alespoň to byl můj důvod, proč jsem si vybral toto chování. (Pokud chcete říct, že ' m čtu příliš mnoho, řekněte, proč byste si toto chování vybrali explicitně. Rozhodl jsem se pokračovat ve čtení, aby napodobil gets() pokud to ' nenapsalo přes váš buffer.)
  • @ CAD97 Rozumím, že vaše námitka se týká mé první odrážky. Váš kód skutečně napodobuje gets a skutečně ' nepřetéká vyrovnávací paměť.Chci říct, že gets je rozdělen více než jedním způsobem. getline víceméně je všechny řeší.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *