Je veux lire l'entrée d'un utilisateur et l'enregistrer. Ce que j'ai maintenant fonctionne mais j'ai besoin de savoir si c'est légitime (suivant la norme ansi - c90) que scanf attribue d'abord la variable "longueur" avant d'allouer de la mémoire pour l'entrée, ou si c'est juste une bizarrerie du compilateur.
#include <stdio.h> int main() { char* text; int length = 0; scanf("%s%n", text = malloc(length+1), &length); printf("%s", text); return 0; }
3 Réponses :
Cela ne fonctionnera pas comme prévu.
Au moment où vous appelez malloc
, length
a toujours la valeur 0, donc vous n'allouez qu'un octet. length
n'est mis à jour qu'après le retour de scanf
. Ainsi, toute chaîne non vide écrira au-delà des limites du tampon alloué, en invoquant comportement indéfini .
Bien que ce ne soit pas exactement la même chose, ce que vous pouvez faire est d'utiliser getline
, en supposant que vous utilisez un système POSIX tel que Linux. Cette fonction lit une ligne de texte (y compris la nouvelle ligne) et alloue de l'espace pour cette ligne.
char *text = NULL; size_t n = 0; ssite_t rval = getline(&text, &n, stdin); if (rval == -1) { perror("getline failed"); } else { printf("%s", text); } free(text);
Hormis le problème évident d'utilisation abusive de scanf
abordé dans une autre réponse, cela ne suit pas non plus aucune norme C:
#include <stdio.h> ... text = malloc(length+1)
Puisque vous ne l'avez pas include stdlib.h
où malloc
est trouvé, C90 supposera que la fonction malloc
a la forme int malloc (int); ce qui est bien sûr absurde.
Et puis lorsque vous essayez d'assigner un int
(le résultat de malloc) à un char *
, vous avez une violation de contrainte de C90 6.3.16.1, les règles d'assignation simple.
Par conséquent, votre code n'est pas autorisé à se compiler proprement, mais le compilateur doit donner un message de diagnostic.
Vous pouvez éviter ce bogue en passant à la norme ISO C.
Problèmes bien expliqués par d'autres
Je veux lire l'entrée d'un utilisateur et l'enregistrer
Pour ajouter et atteindre l'objectif d'OP, un code similaire pourrait faire
int length = 255; char* text = malloc(length+1); if (text == NULL) { Handle_OOM(); } scanf("%255s%n", text, &length); // Reallocate if length < 255 and/or if (length < 255) { char *t = realloc(text, length + 1); if (t) text = t; } else { tbd(); // fail when length == 255 as all the "word" is not certainly read. }
Ce qui précède serait une approche simple si une entrée excessive était jugée hostile. p >
Bien que sur les systèmes hébergés modernes, 256 octets ne soient pas un gros problème à allouer sur la pile ou en tant que mémoire statique, il n'est donc pas nécessaire d'utiliser malloc ici en premier lieu. Sur les systèmes embarqués modernes, malloc ne doit pas non plus être utilisé, pour d'autres raisons.
@Lundin Je m'attendrais à ce que OP alloue dans le cadre d'une fonction de lecture de chaîne / mot et doit renvoyer la mémoire allouée. Vrai, un char text [2555 + 1]
fonctionnerait bien si toutes les utilisations de text
étaient locales.
Non, l'ordre d'évaluation des arguments de fonction n'est pas spécifié, mais
length
est renseigné lors de l'appel àscanf ()
, donc vous ne pouvez l'utiliser qu'après, donc cette construction ne peut jamais travailler du tout.Merci pour votre réponse mais c'est exactement ce que je ne comprends pas. Cela fonctionne avec mingw en tant que compilateur. Mais si ce n'est pas spécifié, c'est toute la réponse dont j'avais besoin. Merci
Vous passez un pointeur sur le champ de longueur à scanf. Scanf le remplira avec la longueur de la chaîne
% s
. Il est donc disponible après que vous avez appelé scanf. Mais vous devez allouer l'espace avecmalloc ()
avant l'appel à scanf. Vous voyez le décalage?Le comportement non défini est désagréable de cette façon. Parfois, cela peut sembler fonctionner, mais la prochaine fois (avec peut-être un petit changement apparemment sans importance), il plantera et brûlera.
Cela fonctionne avec mingw en tant que compilateur. Uniquement pour les définitions très, très vagues de «travail». Puisque
length
est initialisé à zéro, votre code alloue un tampon de 1 octet pour contenir toute chaîne lue. La lecture de quelque chose de plus long qu'une chaîne de zéro octet débordera ce tampon et provoquera un comportement indéfini.essayez de diffuser quelques mégaoctets de données (sans espaces)
de tout ce qui a déjà été dit votre programme suit une logique très étrange, la première chose à obtenir est la chaîne et la seconde sa longueur, l'ordre logique est d'obtenir d'abord la longueur puis la chaîne
@bruno Mais avec scanf (), le
% n
doit être après le% s
pour que le paramètre soit rempli avec la longueur de la chaîne.Même si
length
a été défini plus tôt, utiliser le résultat demalloc ()
avant de tester s'il est nul est bien trop dangereux à considérer.@Ctx la logique lit la longueur, malloc et vérifie le résultat malloc, puis lit la chaîne
@bruno Hehe;) Comment comptez-vous lire la longueur sans lire la chaîne? ;))
@ctx bien sûr la longueur doit être disponible avant la chaîne, le chemin logique doit être des deux côtés ^^
"Je veux lire l'entrée d'un utilisateur et l'enregistrer" - et vous vous attendez à ce que cet utilisateur compte ses caractères et entre la valeur correcte? Qu'est-ce que pour l'interface utilisateur.
Il n'est pas nécessaire que vous écriviez un code aussi déroutant. Écrivez simplement un code structuré clair et passez à autre chose.