I “m egy olyan osztályban, amely C-t használ, és az oktatóm sajnos a gets()
elemet használta a mintakódban.
Mivel ez nyilvánvalóan szörnyű mulasztás, valószínűleg meghatározatlan viselkedést és egyéb problémákat okoz (csak egy kicsit szarkazmus), úgy döntöttem, hogy megvalósítom gets_s()
, mert szórakoztató gyakorlat volt, és néha egyszerűen nem ér teljes hibajelzés a fgets()
használatával, és csak váratlanul hosszú sorokat akar csonkítani.
Nem érdekel, hogy ez teljes mértékben megvalósítja-e a gets_s()
a C11 szabványban meghatározottak szerint – ez csak állítólag az gets()
helyettesítője, amely nem túllépi a puffert.
Azonban az a nagyon fontos, hogy ez a funkció valóban azt tegye, amit hirdet: biztonságban van és nem túllépi a puffert.
Ez az én fi első munka a C-ben (általában a java vagy kotlin alkalmazást használom), és mindent értékelek tippek, bár szeretnék legalább néhányat említeni ennek a kódnak a biztonságáról, és érdekelne a hordozhatóság is (a jelenlegi fordítók felé).
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; }
és egy kis tesztfájl:
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; }
Sikeresen fordít a gcc -Wall -Wextra -Wpedantic -Werror gets_s.c
alkalmazással, így a “plusz”.
Válasz
Először ne hívjuk gets_s
-nek, mivel az aláírás és a viselkedés finom és nem túl finom módon különbözik egymástól, éppen ezért zavartsághoz és csalódottsághoz vezet. Egyébként tényleg nem szeretné a gets_s
-szerződést.
Nevezzen valami leírónak, például: getline_truncated
.
Tudja, hogy a n <= 0
UB az Ön implementációjában?
if (temp == EOF || temp = "\n") ^
Biztos vagyok benne, hogy fordítója figyelmeztet a fenti elgépelésre.
Vagy ne kérjen tőle minden figyelmeztetést (-Wall -Wextra -std=...
)?
Javaslom a visszatérési kódok újradefiniálása és átnevezése a jobb eredménytesztelés érdekében:
#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
Így tesztelheted a ==0
vagy >=0
attól függően, hogy mit dönt “sikernek”, illetve !=0
vagy <0
a “kudarcért”.
Megjegyzések
Válasz
-
A kód túl sokat olvashat:
temp = fgetc( stdin ); if (temp == EOF || temp = "\n") return GETS_S_OK;
Ha a
temp
nemEOF
és\n
, a karakter elveszett. Jobb lenneungetc()
. -
A kód meghaladja a megbízást. A hurok
do temp = fgetc( stdin ); while ( temp != EOF && temp != "\n" );
garantálja, hogy a hívó soha nem fog üres karakterláncot látni. Az üres karakterláncok néha szemantikailag értelmesek.
-
man fgets
:A fgets () és a get () függvények nem tesznek különbséget a fájl vége és a hiba között, és a hívóknak a feof ( 3) és a ferror (3) segítségével meghatározhatja, hogy melyik történt.
Kiváló helyzetben van éppen ezzel. A vak elválaszolás helyett a
GETS_S_ERROR
, határozza meg, mi történt, és ennek megfelelően térjen vissza. Pl.#define GETS_S_EOF 3
.
Megjegyzések
- I ' m utánozva
gets()
, hogy I ' m a sor végéig olvasva, az ötlet az, hogy ' az, amit valaki, aki használta volna akar. Ha meg akarja állítani az n-mínusz-egy karakter viselkedést, használja afgets()
parancsot. Vagy legalábbis ez volt az oka annak, hogy ezt a viselkedést válasszam. (Ha azt akarod mondani, hogy túl sokat olvasok ' m, mondd meg, miért választanád kifejezetten ezt a viselkedést. Úgy döntöttem, hogy folytatom az olvasást, hogy utánozzam agets()
ha nem írta ' t a puffer elé.) - @ CAD97 Megértem, hogy kifogása az első felsorolásomra vonatkozik. A kódod valóban utánozza a következőt:
gets
, és valóban nem ' t túlcsordítja a puffert.Az a véleményem, hogy azgets
többféleképpen is meg van törve.getline
többé-kevésbé megoldja mindet.
temp = "\n"
elírási hiba ebben csak bejegyzés; Eredetileg egy külön gépre írtam a kódot, majd újra begépeltem a kérdéshez. Bizonyára ' biztos voltam benne, hogy része lett volna a kódomnak uld hibáztak (ahogy összeállítottam a-Wall -Wextra -Wpedantic -Werror
fájlmal.'\n' == temp
– így, ha==
gépel=
gépelnek, sokkal határozottabb hibát kap