Je « m dans une classe qui utilise C, et mon instructeur a Malheureusement utilisé gets()
dans un exemple de code.
Comme il sagit manifestement dun oubli odieux, susceptible de provoquer un comportement non défini et dautres problèmes divers (seulement un peu sarcasme), jai décidé de mettre en œuvre gets_s()
, car cétait un exercice amusant et parfois ça ne vaut pas la peine pour faire une vérification derreur complète avec fgets()
et vous voulez simplement tronquer de manière inattendue les longues lignes.
Je ne « mest pas préoccupé de savoir si cela implémente pleinement gets_s()
comme spécifié dans la norme C11 – ceci est juste censé être un remplacement de gets()
qui ne dépasse pas votre tampon.
Cependant, ce qui est très important, cest que cette fonction fait réellement ce quelle annonce: elle est sûre et ne dépasse pas la mémoire tampon.
Cest ma fi première fois à travailler en C (jutilise généralement java ou kotlin ), et japprécie tout conseils, bien que je « voudrais au moins une mention de la sécurité de ce code, et je suis également intéressé par la portabilité (vers les compilateurs actuels).
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; }
et un petit fichier de test:
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; }
Il se compile avec succès avec gcc -Wall -Wextra -Wpedantic -Werror gets_s.c
, de sorte que « sa plus.
Réponse
Premièrement, ne lappelez pas gets_s
car la signature et le comportement diffèrent de manière subtile et pas si subtile, cela conduit à la confusion et à la frustration. Quoi quil en soit, vous vraiment ne voudriez pas du contrat gets_s
.
Appelez-le quelque chose de descriptif comme getline_truncated
.
Savez-vous que n <= 0
est UB dans votre implémentation?
if (temp == EOF || temp = "\n") ^
Je suis sûr que votre compilateur vous avertit de la faute de frappe ci-dessus.
Ou ne lui demandez-vous pas tous les avertissements (-Wall -Wextra -std=...
)?
Je suggère redéfinir et renommer vos codes de retour pour permettre un meilleur test des résultats:
#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
De cette façon, vous pouvez tester ==0
ou >=0
selon ce que vous décidez comme « succès », respectivement !=0
ou <0
pour « échec ».
Commentaires
Réponse
-
Le code peut lire trop:
temp = fgetc( stdin ); if (temp == EOF || temp = "\n") return GETS_S_OK;
Si
temp
nest niEOF
ni\n
, le personnage est perdu. Vous « d mieuxungetc()
. -
Le code dépasse le mandat. La boucle
do temp = fgetc( stdin ); while ( temp != EOF && temp != "\n" );
garantit que lappelant ne verra jamais une chaîne vide. Parfois, les chaînes vides sont sémantiquement significatives.
-
man fgets
:Les fonctions fgets () et gets () ne font pas la distinction entre fin de fichier et erreur, et les appelants doivent utiliser feof ( 3) et ferror (3) pour déterminer ce qui sest produit.
Vous êtes bien placé pour faire exactement cela. Au lieu de renvoyer aveuglément
GETS_S_ERROR
, déterminez ce qui sest passé et renvoyez en conséquence. Par exemple,#define GETS_S_EOF 3
.
Commentaires
- Je ' m imitant
gets()
en ce que je ' je lis jusquau bout de la ligne, lidée étant que ' est ce que quelquun qui aurait utilisé veut. Si vous voulez le comportement stop-à-n-moins-un-caractères, utilisez simplementfgets()
. Ou du moins, cétait la raison pour laquelle jai choisi ce comportement. (Si vous voulez dire que je ' que je lis trop, expliquez pourquoi vous choisiriez ce comportement explicitement. Jai choisi de continuer à lire pour imitergets()
si cela na pas ' t écrire au-delà de votre tampon.) - @ CAD97 Je comprends que votre objection concerne mon premier point. Votre code imite en effet
gets
, et il ne ' pas de débordement du tampon.Ce que je veux dire, cest quegets
est rompu de plusieurs manières.getline
les résout plus ou moins tous.
temp = "\n"
est une faute de frappe dans ce post seulement; jai initialement écrit le code sur une machine distincte, puis je lai retapé pour la question. ' je suis sûr que cela faisait partie de mon code, il wo uld ont fait une erreur (comme je lai compilé avec-Wall -Wextra -Wpedantic -Werror
.'\n' == temp
– de cette façon, sils tapent==
à=
, vous obtenez une erreur beaucoup plus précise