40 votes

Boucle For avec pointeur en C

J'apprends le C et j'ai une question sur une for avec un pointeur à l'intérieur.

Je ne comprends pas ce qu'un pointeur fait dans le for boucle. Que fait le *p dans la boucle suivante ?

char str[128] = "Some Text";
char *p;

for (p = str; *p /*what does this mean?*/; p++)
{
    // Code
}

Je comprends le reste, mais pourquoi n'est pas *p comme p > 3 ou quelque chose comme ça ?
Pourquoi est-il seul ?
Pourquoi est-ce écrit de cette façon ?

53voto

errikos Points 732

Dans un contexte booléen tel que la condition d'une for chaque expression en C est évaluée à true (non-zéro) ou false (zéro).

Vous voulez le for pour qu'elle se termine, lorsqu'elle atteint la fin de la chaîne.

En C, chaque chaîne se termine par le caractère '\0' qui est pratiquement 0 . Ainsi, lorsque le for La boucle atteint la fin de la chaîne, *p évalue à '\0' qui est 0 qui vaut false, ce qui met fin à l'opération. for boucle.

31voto

Manos Nikolaidis Points 7375

La boucle for se terminera si ce qui se trouve entre les deux ; dans l'énoncé est zéro (faux). *p déréférence p et renvoie le char , p pointe. Selon Dennis Ritchie "C traite les chaînes de caractères comme des tableaux de caractères terminés conventionnellement par un marqueur" . Ce marqueur est le caractère nul dont la valeur (ASCII) est zéro. Donc, cette boucle for :

for (p = str; *p; p++)

est équivalent à ces

for (p = str; *p != '\0'; p++)
for (p = str; *p != 0; p++)
for (p = str; p[0] != '\0'; p++)

Un autre nom pour le caractère de fin de nullité est sentinelle ou selon Donald Knuth "valeur fictive" (Art de la programmation informatique, Volume 1). Voici un diagramme de la str les indices (décalages par rapport au début) de chaque caractère et les valeurs à chaque indice :

enter image description here

Pour être complet et après une demande aux commentaires, voici ce que le débogueur voit dans le bloc mémoire qui str occupe :

0x00007fffffffe6a0:
  0x53 0x6f 0x6d 0x65 0x20 0x54 0x65 0x78 0x74 0x00 0x00 0x00 0x00 0x00 0x00 0x00
     S    o    m    e         T    e    x    t
  1. La valeur hexadécimale de la première ligne est l'adresse (64 bits) de ce bloc mémoire. C'est là que se trouve p pointe au début de la boucle for.
  2. Sur la 2ème ligne, vous voyez les valeurs hexadécimales des lettres de votre chaîne. Vous pouvez voir une table ASCII aquí . Le dernier caractère de votre chaîne est t avec une valeur hexagonale de 0x74 . Ensuite, vous avez le caractère nul de la chaîne de caractères. 0x00 . Ensuite, vous voyez quelques caractères nuls supplémentaires parce que j'ai construit en mode débogage et que le compilateur a réinitialisé à zéro. Normalement, vous devriez voir des déchets (des valeurs apparemment aléatoires).
  3. Sur la 3ème ligne, j'ai ajouté les caractères de votre chaîne de caractères pour référence.

Je comprends que vous êtes en ce moment en train d'apprendre rapidement à utiliser les pointeurs en C, mais un jour vous serez capable de dire " Je C le point "

19voto

KeylorSanchez Points 838

Cela pourrait être réécrit comme suit

for (p = str; *p != '\0'; p++)
{
    // Code
}

En C, une chaîne de caractères doit toujours être terminée par un caractère nul, qui est identique à ' \0 ou 0 .

14voto

haccks Points 33022

Avant de plonger dans le vif du sujet, j'aimerais énoncer une règle simple en C concernant une expression

Lorsque C exige le Valeur booléenne d'une expression , a false La valeur est déduite lorsque l'expression est égale à zéro et un true valeur sinon. Autrement dit, chaque fois que l'on écrit

if(expr)

donde expr est une expression quelconque, le compilateur agit essentiellement comme si elle avait été écrite en tant que

if((expr) != 0)  

Venons-en maintenant à votre question :

Que fait le *p dans la boucle suivante ?

En C, les chaînes de caractères sont terminées par un caractère nul. '\0' .

enter image description here

Chaque caractère a un équivalent décimal. Le site '\0' est un Caractère d'échappement ASCII . L'équivalent décimal de '\0' es 0 .

Donc, l'expression *p dans la boucle, il suffit de vérifier que l'équivalent décimal du caractère à l'adresse mémoire pointée par p est soit un zéro, soit un non-zéro. Lorsque p atteint la fin de la chaîne de caractères et trouve le premier '\0' l'expression *p renvoie à 1 une valeur nulle. Un zéro signifie false en C. Ceci est équivalent à tester *p != '\0' o *p != 0 comme indiqué ci-dessus.

Voici comment cela fonctionne :

enter image description here


<strong>1 </strong>Lorsque <code>p</code> évalue alors la valeur de <code>p</code> est récupérée de la mémoire. Cette valeur est la valeur de l'expression <code>*p</code> .

13voto

Zaibis Points 1616

Analysons-le de manière sèche mais profonde !

Ou comme dirait D. Ritchie : Faisons-le avec la puissance du langage d'assemblage et la commodité du langage d'assemblage.


Je vais essayer d'expliquer tous les aspects nécessaires en me référant à la norme ISO/IEC:9899 (c'est moi qui souligne) - C99. (Le style du post est motivé par la phrase de Donald Knuth "La science est ce que nous comprenons suffisamment bien pour l'expliquer à un ordinateur. L'art est tout ce que nous faisons par ailleurs. " )

Tout d'abord, examinons ce qu'est exactement la for -La boucle est censée faire !

Se référer à la norme ISO/IEC:9899 6.8.5 "Déclarations d'itération".

Sémantique

4 Une instruction d'itération entraîne l'exécution répétée d'une instruction appelée corps de boucle jusqu'à ce que l'expression de contrôle soit égale à 0 .

Jusqu'ici, rien de nouveau, je suppose, alors allons-y :

6.8.5.3 L'instruction for

1 La déclaration for ( clause-1 ; expression-2 ; expression-3 ) statement

se comporte comme suit : L'expression l'expression-2 est l'expression de contrôle c'est-à-dire évalué avant chaque exécution du corps de la boucle. ...

Donc nous savons maintenant que le corps (dans votre cas // Code ) sera exécuté tant que la valeur préalablement évaluée de votre *p est différent de zéro.

... L'expression l'expression-3 est évaluée comme une expression vide après chaque exécution du corps de la boucle. [...]

Donc, maintenant nous savons, (je suppose que creuser jusqu'à p++ n'est pas nécessaire ?!) que pour chaque itération p par incréments, de sorte qu'il peut y avoir un changement de *p .

Le point suivant n'est pas lié, mais je l'ajoute car il fait de la partie sémantique de l'accord un élément important de l'accord. for complet et son bien de savoir depuis sa la raison, pourquoi for(;;) est une inf-boucle.

2 (---) La clause-1 et l'expression-3 peuvent être omises. Une expression-2 omise est remplacée par une constante non nulle.

Ok, c'est la partie sèche mais enrichie d'informations de ce que la for La boucle le fait dans votre cas.

Passons maintenant à l'arithmétique des pointeurs :

6.5.6 Opérateurs additifs

Contraintes

2 Pour l'addition, soit les deux opérandes doivent être de type arithmétique, soit un opérande doit être un pointeur vers un objet et l'autre doit être de type entier. ( Incrémenter est équivalent à ajouter 1. )

Dans votre cas, vous ajoutez donc 1 (entier) au type "pointeur vers un objet".

Qu'est-ce qui équivaut à augmenter l'adresse de la taille de son type pointé comme le montre cette image de tomislav kostic :

CC BY-SA 3.0 by tomislav kostic

Maintenant, voyons ce que *p le fait réellement.

6.5.3.2 Opérateurs d'adresse et d'indirection

Contraintes

[...]

2 L'opérande de l'opérateur unaire * doit être de type pointeur.

Sémantique

[...]

4 L'opérateur unaire * indique l'indirection. Si l'opérande pointe vers une fonction, le résultat est un désignateur de fonction ; si il pointe vers un objet, le résultat est une lvalue désignant l'objet. . Si l'opérande a le type ''pointeur vers le type'', le résultat a le type ''type''. Si une valeur invalide a été attribuée au pointeur, le comportement de l'opérateur unaire * est indéfini.

C'est encore un peu sec. 1 mais pour mieux comprendre, on peut faire de l'ingénierie inverse :

6.5.2.1 Subscription de tableau

[...]

Sémantique

2 Une expression postfixe suivie d'une expression entre crochets [] est une désignation en indice d'un élément d'un objet tableau. La définition de l'opérateur d'indice [] est que E1[E2] est identique à (*((E1)+(E2))) .

Así que *((p)+(0)) ce qui est (depuis p+0 est identique à p ... évident) est égal à p[0] ne fait rien d'autre que d'évaluer p de l'objet.

Et comme nous le savons, expression-2 d'une boucle for est d'interrompre l'itération si elle évalue 0 on peut dire que c'est la même chose que p[0] != 0 .

Maintenant, la dernière étape

Regardons simplement l'ami du codeur ; JSSCA ... Non, attendez... notre ami a été appelé... ASCII Maintenant que c'est clarifié, nous pouvons comprendre ce que le 0 représente.

C'est le jeton NULL qui, en C, désigne la fin d'une chaîne de caractères.


Si concluant :

Tout ce qu'on fait, c'est :

Iterating the body of that for -boucle, jusqu'à p pointe en fait vers l'adresse, où l'objet est évalué comme le jeton de fin de chaîne.

Ou :

Soit p parcourir la chaîne jusqu'à ce que la fin soit atteinte.


Et maintenant, juste pour me citer moi-même, quelque chose que vous ne devriez jamais oublier :
(souligné par moi.....)

Une variable est déclarée par le biais d'un déclarateur( spécificateur de type ) qui précède l'identifiant qui nomme un objet lvalue qui peut être évalué à sa valeur

Ce n'est ni plus ni moins !


1 C'est-à-dire, ce que j'ai promis ! ;)

Prograide.com

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.

Powered by:

X