Lire une chaîne avec scanf

147

Je suis un peu confus à propos de quelque chose. J'avais l'impression que la manière correcte de lire une corde en C scanf()allait dans le sens de

(sans parler du possible dépassement de tampon, ce n'est qu'un simple exemple)

char string[256];
scanf( "%s" , string );

Cependant, ce qui suit semble fonctionner aussi,

scanf( "%s" , &string );

Est-ce juste mon compilateur (gcc), pure chance ou autre chose?

Abeln
la source
Dans le second cas, il n'y a en fait aucun débordement de tampon possible, car vous n'utilisez pas du tout ce tampon. Soit cela, soit vous pourriez dire que toute chaîne de plus de 3 caractères débordera de votre "tampon".
TED
Je faisais référence au premier exemple. En outre, d'autres ont déjà souligné ce qui se passe ici.
abeln
Ouaip. Je l'ai essayé, et Gareth a raison. Bizarre.
TED
Étant donné que cette question est renvoyée par des recherches sur "comment lire une chaîne avec scanf", il peut être approprié de souligner que cette question concerne les moyens de passer le pointeur vers le tampon vers scanf, et que la question et la réponse acceptée se concentrent sur cela, et omettez les restrictions cruciales pour la longueur d'entrée maximale qui devraient être utilisées dans le code réel (mais sont d'ailleurs le point de cette question).
Arkku le

Réponses:

141

Un tableau "se désintègre" en un pointeur vers son premier élément, donc scanf("%s", string)équivaut à scanf("%s", &string[0]). En revanche, scanf("%s", &string)passe un pointeur vers char[256], mais il pointe vers le même endroit.

Ensuite scanf, lors du traitement de la queue de sa liste d'arguments, essaiera d'extraire un fichier char *. C'est la bonne chose lorsque vous avez passé stringou &string[0], mais lorsque vous êtes passé, &stringvous dépendez de quelque chose que la norme de langage ne garantit pas, à savoir que les pointeurs &stringet &string[0]- les pointeurs vers des objets de types et de tailles différents commencer au même endroit - sont représentés de la même manière.

Je ne crois pas avoir jamais rencontré un système sur lequel cela ne fonctionne pas, et dans la pratique, vous êtes probablement en sécurité. Néanmoins, c'est faux et cela pourrait échouer sur certaines plates-formes. (Exemple hypothétique: une implémentation de «débogage» qui inclut des informations de type avec chaque pointeur. Je pense que l'implémentation C sur les «machines Lisp» de Symbolics a fait quelque chose comme ça.)

Gareth McCaughan
la source
5
+1. Il est également trivial de vérifier que la décroissance du tableau &stringfonctionne de la même manière que string(au lieu d'entraîner une corruption aléatoire de la mémoire, comme l'affirment à tort d'autres réponses):printf("%x\n%x\n", string, &string);
Josh Kelley
@Josh Kelley intéressant, je considérerais alors que ce ne serait pas le cas avec un pointeur vers une chaîne allouée via malloc ()?
abeln
4
@abeln: C'est exact. Pour une chaîne allouée via malloc (), stringest un pointeur et &stringest l'adresse de ce pointeur, donc les deux ne sont PAS interchangeables. Comme l'explique Gareth, même pour le cas de la décroissance du tableau, stringet ne &stringsont techniquement pas les mêmes types (même s'ils se trouvent être interchangeables pour la plupart des architectures), et gcc donnera un avertissement pour votre deuxième exemple si vous allumez -Wall.
Josh Kelley
@Josh Kelley: merci, je suis content d'avoir pu clarifier mon esprit à ce sujet.
abeln
1
@JCooper str, & str [0] représentent tous deux la même chose (le début du tableau), et bien que ce ne soit pas nécessairement le cas, & str est également la même adresse mémoire. Maintenant, strPtr pointe vers str afin que l'adresse mémoire stockée à l'intérieur de strPtr soit la même que str et c'est 12fe60. Enfin & strPtr est l'adresse de la variable strPtr, ce n'est pas la valeur stockée dans strptr mais l'adresse mémoire réelle de strPtr. Puisque strptr est une variable différente de toutes les autres, il a également une adresse mémoire différente, dans ce cas 12fe54
Juan Besa
-9

Je pense que ce ci-dessous est exact et cela peut aider. N'hésitez pas à le corriger si vous trouvez des erreurs. Je suis nouveau chez C.

char str[]  
  1. tableau de valeurs de type char, avec sa propre adresse en mémoire
  2. tableau de valeurs de type char, avec sa propre adresse en mémoire autant d'adresses consécutives que d'éléments du tableau
  3. y compris le caractère nul de terminaison '\0' &str, &str[0]et str, tous les trois représentent le même emplacement en mémoire qui est l'adresse du premier élément du tableaustr

    char * strPtr = & str [0]; // déclaration et initialisation

Sinon, vous pouvez diviser cela en deux:

char *strPtr; strPtr = &str[0];
  1. strPtr est un pointeur vers un char
  2. strPtr points au tableau str
  3. strPtr est une variable avec sa propre adresse en mémoire
  4. strPtr est une variable qui stocke la valeur de l'adresse &str[0]
  5. strPtr sa propre adresse en mémoire est différente de l'adresse mémoire qu'elle stocke (adresse du tableau en mémoire aka & str [0])
  6. &strPtr représente l'adresse de strPtr elle-même

Je pense que vous pouvez déclarer un pointeur vers un pointeur comme:

char **vPtr = &strPtr;  

déclare et initialise avec l'adresse du pointeur strPtr

Vous pouvez également diviser en deux:

char **vPtr;
*vPtr = &strPtr
  1. *vPtr pointe sur le pointeur strPtr
  2. *vPtr est une variable avec sa propre adresse en mémoire
  3. *vPtr est une variable qui stocke la valeur de l'adresse & strPtr
  4. dernier commentaire: vous ne pouvez pas faire str++, l' stradresse est un const, mais vous pouvez le fairestrPtr++
Angelita
la source