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ä tai ), 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

  • 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.
  • @ CAD97 Siksi monet oikeutta ja turvallisuutta arvostavat C-ohjelmoijat olisivat kirjoittaneet '\n' == temp – tällä tavalla, jos he kirjoittavat == kirjoitusvirheeseen =, saat paljon selvemmän virheen

Vastaa

  • Koodi saattaa lukea liikaa:

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

    Jos temp ei ole EOF eikä \n , merkki on kadonnut. Sinun on parempi ungetc() 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ä vain fgets(). 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äni gets on rikki useammalla kuin yhdellä tavalla. getline ratkaisee ne kaikki.

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *