I ”m luokassa, joka käyttää C: tä, ja ohjaajani on valitettavasti käytti gets()
esimerkkikoodissa.
Koska tämä on ilmeisesti hirvittävä valvonta, joka todennäköisesti aiheuttaa määrittelemätöntä käyttäytymistä ja muita erilaisia ongelmia (vain vähän sarkasmi), päätin toteuttaa gets_s()
, koska se oli hauska harjoitus ja joskus sen arvoinen se suorittaa täydellisen virheen tarkistuksen fgets()
-toiminnolla ja haluat vain katkaista odottamattomasti pitkät linjat.
En ole huolissani siitä, toteutetaanko tämä kokonaan gets_s()
kuten C11-standardissa määritetään – tämän oletetaan olevan vain drop-in-korvike gets()
-palvelulle, joka ei ylitä puskuriasi.
Tärkeää on kuitenkin se, että tämä toiminto todella tekee sen, mitä se mainostaa: se on turvallinen eikä ylitä puskuria.
Tämä on minun fi ensimmäinen kerta työskentely C: ssä (käytän yleensä java tai kotlin ), ja arvostan kaikkia vinkkejä, vaikka haluaisin ainakin mainita tämän koodin turvallisuuden, ja olen kiinnostunut myös siirrettävyydestä (nykyisille kääntäjille).
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; }
ja pieni testitiedosto:
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 kääntyy onnistuneesti gcc -Wall -Wextra -Wpedantic -Werror gets_s.c
-palvelun kanssa siten, että ”sa plus”.
Vastaa
Ensinnäkin, älä kutsu sitä gets_s
, koska allekirjoitus ja käyttäytyminen eroavat toisistaan hienovaraisesti ja ei niin hienovaraisesti, että vain johtaa sekaannukseen ja turhautumiseen. Joka tapauksessa todella et halua gets_s
-sopimusta.
Kutsukaa sitä kuvailevaksi, kuten getline_truncated
.
Tiedätkö, että n <= 0
on UB toteutuksessa?
if (temp == EOF || temp = "\n") ^
Olen varma, että kääntäjäsi varoittaa sinua yllä olevasta kirjoitusvirheestä.
Tai älä kysy kaikilta varoituksilta (-Wall -Wextra -std=...
)?
Ehdotan paluukoodien määritteleminen ja nimeäminen uudelleen parempien tulosten testaamiseksi:
#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ällä tavalla voit testata ==0
tai >=0
riippuen siitä, mitä päätät ”menestykseksi”, vastaavasti !=0
tai <0
”epäonnistumisesta”.
Kommentit
Vastaa
-
Koodi saattaa lukea liikaa:
temp = fgetc( stdin ); if (temp == EOF || temp = "\n") return GETS_S_OK;
Jos
temp
ei oleEOF
eikä\n
, merkki on kadonnut. Sinun on parempiungetc()
se. -
Koodi ylittää valtuutuksen. Silmukka
do temp = fgetc( stdin ); while ( temp != EOF && temp != "\n" );
takaa, että soittaja ei koskaan näe tyhjää merkkijonoa. Joskus tyhjät merkkijonot ovat semanttisesti merkityksellisiä.
-
man fgets
:Fgets () – ja get () -toiminnot eivät tee eroa tiedoston lopun ja virheen välillä, ja soittajien on käytettävä feof ( 3) ja ferror (3) määrittääksesi, mikä tapahtui.
Sinulla on hyvät mahdollisuudet tehdä juuri tämä. Sen sijaan, että palaat sokeasti
GETS_S_ERROR
, selvitä, mitä tapahtui, ja palaa vastaavasti. Esim.#define GETS_S_EOF 3
.
Kommentit
- I ' m matkimalla
gets()
siinä mielessä, että ' m lukee rivin loppuun, ajatuksena on, että ' s mitä joku, joka olisi käyttänyt haluaa. Jos haluat stop-at-n-miinus yhden merkin käyttäytymisen, käytä vainfgets()
. Tai ainakin se oli minun syy valita tämä käyttäytyminen. (Jos haluat sanoa, että luen liikaa ', sano miksi valitsisit tämän käyttäytymisen nimenomaisesti. Päätin jatkaa lukemista jäljittelemällägets()
jos se ei ' t kirjoittanut puskurisi ohi.) - @ CAD97 Ymmärrän, että vastustuksesi kohdistuu ensimmäiseen luettelomerkkini. Koodisi jäljittelee todellakin
gets
, eikä todellakaan ' t ylitä puskuria.Mielestänigets
on rikki useammalla kuin yhdellä tavalla.getline
ratkaisee ne kaikki.
temp = "\n"
on kirjoitusvirhe tässä vain viesti; Kirjoitin koodin alun perin erilliselle koneelle ja kirjoitin sen sitten uudelleen kysymykseen. Olen ' varma, olisiko se ollut osa koodiani uld ovat virheellisiä (kuten käännin-Wall -Wextra -Wpedantic -Werror
-koodilla.'\n' == temp
– tällä tavalla, jos he kirjoittavat==
kirjoitusvirheeseen=
, saat paljon selvemmän virheen