Je veux savoir quel désavantage de scanf()
existe.
Dans de nombreux sites, j'ai lu que l'utilisation de scanf
entraînerait parfois un débordement de tampon. Quelle en est la raison et y a-t-il d'autres inconvénients avec scanf
?
Je veux savoir quel désavantage de scanf()
existe.
Dans de nombreux sites, j'ai lu que l'utilisation de scanf
entraînerait parfois un débordement de tampon. Quelle en est la raison et y a-t-il d'autres inconvénients avec scanf
?
La plupart des réponses semblent se concentrer sur la chaîne de débordement de la mémoire tampon d'émission. En réalité, les spécificateurs de format qui peut être utilisé avec scanf
les fonctions de soutien explicite de la largeur de champ , ce qui limite la taille maximale de l'entrée et de prévenir le débordement de la mémoire tampon. Ce qui rend le populaire accusations de chaîne-dépassement de la mémoire tampon les dangers présents dans scanf
pratiquement sans fondement. Affirmant qu' scanf
est en quelque sorte analogue à l' gets
dans le respect n'est plus tout à fait incorrecte. Il y a une importante différence qualitative entre scanf
et gets
: scanf
ne fournir à l'utilisateur avec de la ficelle-buffer-overflow-la prévention des fonctionnalités, en gets
ne le fait pas.
On peut dire que ces scanf
caractéristiques sont difficiles à utiliser, car la largeur de champ doit être intégré dans la chaîne de format (il n'y a pas moyen de passer au travers d'un variadic argument, comme cela peut être fait en printf
). Qui est réellement vrai. scanf
est en effet plutôt mal conçue à cet égard. Mais néanmoins, toute réclamation scanf
est en quelque sorte irrémédiablement brisé à l'égard de la chaîne-buffer overflow de sécurité sont complètement faux et généralement faites par des programmeurs paresseux.
Le vrai problème avec scanf
a une nature totalement différente, même si elle est aussi à propos de débordement. Lors de l' scanf
fonction est utilisée pour la conversion de décimal représentations des nombres dans les valeurs de l'arithmétique types, il n'offre aucune protection de dépassement de capacité arithmétique. Si dépassement de capacité se produit, scanf
produit un comportement indéfini. Pour cette raison, la seule bonne façon d'effectuer la conversion de la bibliothèque standard C est des fonctions de strto...
de la famille.
Donc, pour résumer ce qui précède, le problème avec scanf
, c'est qu'il est difficile (mais possible) de l'utiliser correctement et en toute sécurité avec des tampons de chaîne. Et il est impossible de l'utiliser en toute sécurité pour l'arithmétique d'entrée. Ce dernier est le véritable problème. Le premier est juste un inconvénient.
P. S. ci-dessus dans le but de permettre à toute la famille de scanf
fonctions (y compris aussi l' fscanf
et sscanf
). Avec scanf
plus précisément, le problème évident est que l'idée même de l'aide d'une stricte fonction formaté pour la lecture potentiellement interactif d'entrée est plutôt discutable.
Les problèmes avec scanf (au minimum):
%s
pour obtenir une chaîne de caractères à partir de l'utilisateur, ce qui conduit à la possibilité que la chaîne peut être plus longue que votre tampon, causant un débordement.Je préfère de beaucoup l'aide d' fgets
à lire les lignes de sorte que vous pouvez limiter la quantité de données à lire. Si vous avez un 1K de mémoire tampon, et vous lire une ligne dans il avec fgets
vous pouvez dire si la ligne est trop longue par le fait il n'y a pas de résiliation de caractère de saut de ligne (dernière ligne d'un fichier sans un retour à la ligne malgré).
Alors vous pouvez vous plaindre à l'utilisateur, ou d'allouer plus d'espace pour le reste de la ligne (en continu si nécessaire jusqu'à ce que vous disposez de suffisamment d'espace). Dans les deux cas, il n'y a pas de risque de débordement de la mémoire tampon.
Une fois que vous avez lu la ligne, vous savez que vous êtes positionné à la ligne suivante, donc il n'y a pas de problème. Vous pouvez alors sscanf
votre chaîne au contenu de votre coeur sans avoir à les enregistrer et de restaurer le pointeur de fichier pour la re-lecture.
Voici un extrait de code que j'utilise fréquemment pour s'assurer qu'aucun dépassement de la mémoire tampon lors de la demande de l'utilisateur pour plus d'informations.
Il pourrait être facilement ajustés pour utiliser un autre fichier que l'entrée standard si nécessaire et vous pourriez aussi avoir d'attribuer ses propres tampon (et continuer à augmenter jusqu'à ce qu'il est assez grand) avant de donner que de retour à l'appelant (bien que l'appelant serait alors responsable de la libération, bien sûr).
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
// Test program for getLine().
int main (void) {
int rc;
char buff[10];
rc = getLine ("Enter string> ", buff, sizeof(buff));
if (rc == NO_INPUT) {
// Extra NL since my system doesn't output that on EOF.
printf ("\nNo input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long [%s]\n", buff);
return 1;
}
printf ("OK [%s]\n", buff);
return 0;
}
Un essai:
$ ./tstprg
Enter string>[CTRL-D]
No input
$ ./tstprg
Enter string> a
OK [a]
$ ./tstprg
Enter string> hello
OK [hello]
$ ./tstprg
Enter string> hello there
Input too long [hello the]
$ ./tstprg
Enter string> i am pax
OK [i am pax]
À partir de la comp.lang.c FAQ: Pourquoi tout le monde dis de ne pas utiliser scanf? Que dois-je utiliser à la place?
scanf
a un certain nombre de problèmes-voir les questions 12.17, 12.18 un, et 12.19. Aussi, son%s
format a le même problème quegets()
a (voir la question 12.23)-il est difficile de garantir que le tampon de réception ne débordera pas. [note de bas de page]Plus généralement,
scanf
est conçu pour être relativement structurée, mise en forme d'entrée (son nom est en fait un dérivé de "scan formaté"). Si vous prêtez attention, il vous dira s'il a réussi ou échoué, mais il peut vous dire, seulement approximativement à l'endroit où il a échoué, et pas du tout comment ou pourquoi. Vous avez très peu de chances d'en faire toute erreur de récupération.Pourtant, l'utilisateur interactif d'entrée est le moins structuré d'entrée, il est. Une interface utilisateur conçue pour permettre la possibilité pour l'utilisateur de taper juste au sujet de n'importe quoi-et pas seulement des lettres ou des signes de ponctuation lorsque les chiffres étaient attendus, mais aussi plus ou moins de caractères que prévu, ou aucun caractère à tous (c'est à dire, juste le RETOUR de la clé), prématuré ou expressions du FOLKLORE ou de quoi que ce soit. Il est presque impossible de s'occuper gracieusement avec tous ces problèmes potentiels lors de l'utilisation d'
scanf
; il est beaucoup plus facile de lire les lignes entières (avecfgets
par exemple), puis de les interpréter, soit à l'aide d'sscanf
ou d'autres techniques. (Fonctions commestrtol
,strtok
, etatoi
sont souvent utiles; voir également les questions de 12,16 et 13.6.) Si vous en utilisezscanf
variante, assurez-vous de vérifier la valeur de retour pour s'assurer que le nombre d'éléments ont été trouvés. Aussi, si vous utilisez%s
, assurez-vous de la garde contre le dépassement de la mémoire tampon.Noter, en passant, que les critiques de l'
scanf
ne sont pas nécessairement mises en accusation defscanf
etsscanf
.scanf
lit à partir destdin
, ce qui est généralement interactif clavier et qui est donc le moins de contraintes, conduisant à la plupart des problèmes. Quand un fichier de données est un format connu, d'autre part, il peut être approprié de le lire avecfscanf
. Il est parfaitement approprié pour analyser les chaînes avecsscanf
(tant que la valeur de retour est cochée), parce que c'est tellement facile de reprendre le contrôle, redémarrez la recherche, jetez-le d'entrée si elle n'a pas de match, etc.Autres liens:
Références: K&R2 Secondes de 7,4 p. 159
Il est très difficile d'obtenir des scanf
pour faire la chose que vous voulez. Bien sûr, vous pouvez, mais des choses comme scanf("%s", buf);
sont aussi dangereux que gets(buf);
, comme tout le monde l'a dit.
Comme un exemple, ce que paxdiablo est en train de faire dans sa fonction de lecture peut être fait avec quelque chose comme:
scanf("%10[^\n]%*[^\n]", buf));
getchar();
Permettra de lire une ligne, stocker les 10 premiers non-caractères de saut de ligne en buf
, puis de supprimer tout jusqu'à (et y compris) un retour à la ligne. Donc, paxdiablo de la fonction peut être écrite à l'aide d' scanf
de la façon suivante:
#include <stdio.h>
enum read_status {
OK,
NO_INPUT,
TOO_LONG
};
static int get_line(const char *prompt, char *buf, size_t sz)
{
char fmt[40];
int i;
int nscanned;
printf("%s", prompt);
fflush(stdout);
sprintf(fmt, "%%%zu[^\n]%%*[^\n]%%n", sz-1);
/* read at most sz-1 characters on, discarding the rest */
i = scanf(fmt, buf, &nscanned);
if (i > 0) {
getchar();
if (nscanned >= sz) {
return TOO_LONG;
} else {
return OK;
}
} else {
return NO_INPUT;
}
}
int main(void)
{
char buf[10+1];
int rc;
while ((rc = get_line("Enter string> ", buf, sizeof buf)) != NO_INPUT) {
if (rc == TOO_LONG) {
printf("Input too long: ");
}
printf("->%s<-\n", buf);
}
return 0;
}
L'un des autres problèmes avec scanf
est son comportement en cas de dépassement. Par exemple, lors de la lecture des int
:
int i;
scanf("%d", &i);
le ci-dessus ne peuvent pas être utilisés en toute sécurité en cas de dépassement. Même pour le premier cas, la lecture d'une chaîne de caractères est beaucoup plus simple de le faire avec fgets
plutôt qu'avec scanf
.
Oui, vous avez raison. Il y a une faille de sécurité importante en scanf
de la famille(scanf
,sscanf
, fscanf
..etc) esp lors de la lecture d'une chaîne de caractères, car elles ne prennent pas la longueur de la mémoire tampon (dans lequel ils sont en lecture) en compte.
Exemple:
char buf[3];
sscanf("abcdef","%s",buf);
clairement le tampon buf
peut contenir MAX 3
char. Mais l' sscanf
va essayer de mettre des "abcdef"
provoquant en elle débordement de la mémoire tampon.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.