Je ne comprends pas ce que fait un pointeur dans la boucle for
. Que fait le *p
faire 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-ce pas *p
comme p > 3
ou quelque chose comme ça?
Pourquoi est-il seul?
Pourquoi est-il écrit de cette façon?
Dans un contexte booléen tel que la condition d'une boucle for
, chaque expression en C est évaluée à vrai (non nul) ou faux (zéro).
Vous voulez que la boucle for
se termine lorsqu'elle atteint la fin de la chaîne.
En C, chaque chaîne se termine par le caractère '\0'
, ce qui est pratiquement 0
. Ainsi, lorsque la boucle for
atteint la fin de la chaîne, *p
correspond à '\0'
, lequel est 0
, qui prend la valeur false, ce qui termine la boucle for
.
La boucle for se terminera si ce qui se trouve entre les deux ;
dans l'instruction est zéro (faux). *p
déréférence p et renvoie les points char
, p
vers. Selon Dennis Ritchie "C traite les chaînes comme des tableaux de caractères conventionnellement terminés par un marqueur" . Ce marqueur est le caractère nul avec une valeur (ASCII) de zéro. Donc, ceci pour la boucle:
for (p = str; *p; p++)
est équivalent à ceux-ci
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 nul est sentinelle ou selon Donald Knuth "valeur fictive" (Art de la programmation informatique, volume 1). Voici un diagramme de la chaîne str
, les index (décalages depuis le début) de chaque caractère et les valeurs de chaque index:
Pour être complet et après une demande dans les commentaires, voici ce que le débogueur voit dans le bloc de mémoire que 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
p
pointe au début de la boucle for.t
avec une valeur hexadécimale de 0x74
. Après cela, vous avez le caractère nul de la chaîne 0x00
. Ensuite, vous voyez quelques caractères nuls supplémentaires car j'ai construit en mode débogage et le compilateur initialisé à zéro. Normalement, vous verriez des ordures (valeurs apparemment aléatoires)Je comprends que vous êtes sur une courbe d'apprentissage abrupte en ce moment avec des pointeurs en C, mais finalement vous pourrez dire "Je C le point"
Cela pourrait être réécrit comme ceci
for (p = str; *p != '\0'; p++)
{
// Code
}
En C, une chaîne doit toujours se terminer par un caractère nul, qui est le même que '\ 0' ou 0
.
Avant de plonger, je voudrais énoncer une règle simple en C concernant une expression
Lorsque C requiert la valeur booléenne d'une expression, une valeur de
false
est déduite lorsque l'expression se compare à zéro , et une valeurtrue
sinon. Autrement dit, chaque fois que l'on écritif(expr)
où
expr
est n'importe quelle expression, le compilateur agit essentiellement comme s'il avait été écrit commeif((expr) != 0)
Venons-en maintenant à votre question:
Qu'est-ce que le
*p
faire dans la boucle suivante?
En C, les chaînes se terminent par un caractère nul '\0'
.
Chaque caractère a un équivalent décimal. Cette '\0'
est un caractère d'échappement ASCII . L'équivalent décimal de '\0'
est 0
.
Ainsi, l'expression *p
en boucle vérifie simplement 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 et trouve le premier '\0'
caractère, l'expression *p
résultats 1 une valeur nulle. Un zéro signifie false
en C. Cela équivaut à tester *p != '\0'
ou *p != 0
Comme indiqué ci-dessus.
Voilà comment cela fonctionne:
1 Lorsque *p
évalue alors la valeur de *p
est récupéré de la mémoire. Cette valeur est la valeur de l'expression *p
.
Ou comme dirait D. Ritchie: Faisons-le avec la puissance du langage d'assemblage et la commodité du ... langage d'assemblage.
J'essaierai d'expliquer tous les aspects nécessaires en référençant la norme ISO/IEC: 9899 (c'est moi qui souligne) - la norme C99. (Le style du message est motivé par la phrase de Donald Knuth "La science est ce que nous comprenons assez bien pour expliquer à un ordinateur. L'art est tout ce que nous faisons.")
for
est censée faire exactement!Se référant à ISO/CEI: 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'à présent, rien de nouveau, je suppose, alors allons-y:
6.8.5.3 L'instruction for
1 L'instruction
for ( clause-1 ; expression-2 ; expression-3 ) statement
se comporte comme suit: L'expression expression-2 est l'expression de contrôle qui est évaluée avant chaque exécution du corps de boucle. ...
Nous savons donc maintenant que le corps (dans votre cas // Code
) Sera exécuté tant que la valeur préalablement évaluée de votre *p
N'est pas nulle.
... L'expression expression-3 est évaluée comme une expression vide après chaque exécution du corps de boucle. [...]
Alors maintenant, nous savons, (je suppose que déterrer la définition de p++
N'est pas nécessaire?!) Que pour chaque itération, des incréments de p
, donc il peut y avoir un changement dans *p
.
Le point suivant n'est pas lié, mais je l'ajoute car cela rend la partie sémantique de for
complète et c'est bien à savoir puisque c'est la raison pour laquelle for(;;)
est une boucle inf .
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 fait la boucle for
dans votre cas.
6.5.6 Opérateurs additifs
Contraintes
2 De plus, soit les deux opérandes doivent être de type arithmétique, soit un opérande doit être un pointeur vers un type d'objet et l'autre doit être de type entier. ( L'incrémentation équivaut à ajouter 1. )
Donc, dans votre cas, vous ajoutez 1 (entier) au type "pointeur vers un objet".
Ce qui équivaut à augmenter l'adresse de la taille de son type pointé comme indiqué dans cette image de tomislav kostic :
Voyons maintenant ce que fait *p
.
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 valeur l désignant l'objet . Si l'opérande a le type "pointeur vers type", le résultat a le type "type". Si une valeur non valide a été affectée au pointeur, le comportement de l'opérateur unaire * n'est pas défini.
C'est encore un peu sec1 mais pour une meilleure compréhension, cela peut être inversé par:
6.5.2.1 Indice de tableau
[...]
Sémantique
2 Une expression suffixe 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))) .
Donc, *((p)+(0))
ce qui est (puisque p+0
Est identique à p
... évident) est égal à p[0]
, Ne fait rien d'autre que d'évaluer p
.
Et puisque nous savons que expression-2
D'une boucle for interrompt l'itération si elle évalue 0
, Nous pouvons dire que c'est la même chose que p[0] != 0
.
Regardons simplement l'ami du codeur C; JSSCA ... Non, attendez ... notre ami a été appelé ... ASCII Maintenant que cela 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.
Tout cela, c'est:
Itération du corps de cette boucle for
, jusqu'à ce que p
pointe réellement vers l'adresse, où l'objet est évalué au jeton "fin de chaîne".
Ou:
Laissez p
parcourir la chaîne jusqu'à la fin.
Et maintenant, juste pour me citer; Quelque chose que vous ne devriez jamais oublier:
(c'est moi qui souligne .....)
Ce n'est ni plus ni moins!
1C'est ce que j'ai promis! ;)
Poétiquement, j'ai essayé de représenter la lutte de * p dans la boucle:
Brave C * p (rogrammers)
Dans la boucle du whilederness
Le NUL les arrêtera
Ceci est un poème haïku, il se compose de trois lignes, la première et la dernière ligne ayant 5 syllabes, et la ligne médiane en 7. Un autre exemple de @Samidamaru (un maître poète Haiku, voir le commentaire ci-dessous): Le premier p est égal à str, Alors p est incrémenté, Jusqu'à ce que * p soit NUL.
Ambassadrice de Hour of Code , Jessica Alba
Suivant les conseils imaginaires de Jessica (qui cite D. Knuth (1)), nous allons essayer de voir la signification de * p dans la boucle for:
for (p = str; *p; p++)
Pour atteindre cet objectif, nous examinons d'abord comment l'opérateur unaire "*" fonctionne en C: "L'opérateur unaire * est l'opérateur d'indirection ou de renvoi; lorsqu'il est appliqué à un pointeur, il accède à l'objet vers lequel le pointeur pointe. "(B. Kernighan et D. Ritchie (2))
Donc * p est simplement la valeur pointée par p:
La boucle for est composée de trois instructions:
- p = str
- * p
- p ++
Dans 1. nous assignons le pointeur au tableau str à p. En C, les affectations suivantes ont le même effet:
p = &str[0];
p = str;
"Par définition, la valeur d'une variable ou d'une expression de type tableau est l'adresse de l'élément zéro du tableau" (K & R (2)). De plus, nous avons que "En évaluant a [i], C le convertit immédiatement en * (a + i). …. il s'ensuit que & a [i] et a + i sont identiques "(K&R (2)). Si nous mettons i = 0, nous obtenons les affectations ci-dessus.
Nous pouvons maintenant affirmer qu'au début de la boucle for, p pointe vers le premier élément de str.
Passons au point 2., au cœur de votre question. La deuxième expression de la boucle contrôle la condition de sortie: l'instruction "* p" est évaluée et si est fausse la sortie de boucle. Cela signifie que "* p" est équivalent à "* p! = 0" ou en mots: lorsque la valeur pointée par p est zéro, quittez.
Maintenant, pour comprendre quand * p est zéro, nous rappelons que le tableau str a été initialisé comme suit:
char str[128] = "Some Text";
et: "toutes les constantes de chaîne contiennent un caractère de terminaison nul (\ 0) comme dernier caractère" (gnu-manual). Ainsi, la chaîne réellement stockée en mémoire a un\0 à la fin: "Some Text\0".
Dans la troisième instruction, p ++, le pointeur p est avancé à l'élément suivant du tableau str, ainsi, à la 9ème itération * p est devenu 0 (ou\0, NULL, NUL, voir la réponse de @Joe) et la boucle se termine.
Une image vaut mille mots, voici une représentation graphique de la boucle:
Dans l'extrait de code suivant * p est utilisé de la même manière mais dans une boucle while:
#include <stdio.h>
int main() {
char str[128] = "We all scream for ice cream!";
char *p = str;
// here we see again the loop exit condition *p == '\0'
while(*p) {
printf("%c", *p);
p++;
}
printf("\n");
}
Références
(1) Vol. I, Algorithmes fondamentaux, section 1.1 (1968)
(2) Le langage de programmation C p. 94-99
Il profite du fait que le terminateur de la chaîne (éventuellement trouvé par celui de la boucle) sera un ASCII NUL
, qui est un zéro, ce qui arrive également à évaluer à false , mettant ainsi fin à la boucle for.
Il convient de noter la différence et la similitude entre 0, faux, NULL et ASCII NUL. Voir cette question: Quelle est la différence entre NULL, '\ 0' et
J'ai essayé de satisfaire les désirs de l'attributaire de primes qui ont été mentionnés à plusieurs reprises. Pour rester simple, j'ai limité ma réponse à trois sections de trois lignes chacune, et parce que (comme The Bellman dit dans sa règle des trois) "Ce que je vous dis trois fois est vrai " (le thème de cette réponse).
Technique
La vérité de votre boucle for
la termine lorsque l'expression *p
correspond à 0
et cette évaluation est effectuée avant chaque itération de la boucle, notez qu'en C 0
est faux et tout le reste est vrai - c'est une définition très très étendue dans d'autres mondes!
La variable de pointeur p
est initialisée une fois, pour pointer vers le début du tableau avec p = str
et p
est incrémenté à la fin de chaque itération, donc *p
accède aux éléments successifs du tableau à chaque itération.
L'expression *p
sera donc évalué à 0
(faux) lorsque l'élément de tableau lu par *p
est le 0
ou '\0'
terminateur qui signale la fin d'une "chaîne" C, mais vous ne pouvez pas voir ce zéro dans l'initialisation de str
car il est fourni automatiquement par le compilateur.
Lyrique
Expressions de vérité
Ne sont pas compris par les jeunes
Lire Ritchie et Knuth
Lunatique
Jessica Alba est une belle actrice très bien informée, ayant pris en compte les vérités de l'observation du développement de la technologie informatique, comme le révèlent ces citations:
"Tous les cinq ans, j'ai l'impression d'être une personne complètement différente."
"Tout dépend de votre produit et de ses performances. Soit il fonctionne, soit il ne fonctionne pas."
Il y a longtemps, dans un PDP très, très loin, les ressources étaient rares, les noms étaient courts: i
pour index, p
pour pointeur plairait aux premiers programmeurs Jedi.
Des tests implicites ont révélé la vérité dans l'espace de conditions for
. Un seul *
Était tout ce qu'ils avaient tapé, faisant confiance à p
et le poussant à la fin des chaînes.
À ce jour, ils utilisent la boucle for(e = s;*e;e++)
la plus familière et élégante pour défier l'empire C++ et ses cohortes de ctors, dtors et vils itérateurs. Des bits et des octets nus contre des modèles, des exceptions et des types obscurs, seuls les courageux osent encore que C se batte, et dévoilent le void *
.
Un haïku:
WHY for (p=str; *p; p++)
IS for (p=str; p[0] != 0; p++)
THINK for (i=0; str[i]; ++i)
MODIFIÉ
Voici quelques détails supplémentaires:
La deuxième ligne de code du "haïku" est équivalente à la première ligne. Le message d'origine demande "qu'est-ce que cela signifie" dans un commentaire de code. La deuxième ligne illustre la réponse par équivalence. * p signifie p [0]. La deuxième clause de la boucle for se soucie de savoir si p [0] est ou non équivalent à zéro.
La troisième ligne de code du "haïku" est une ligne de code qui peut être utilisée de manière conceptuelle: vous pouvez penser que le fonctionnement de la ligne d'origine se comporte comme la troisième ligne.
Comme vous pouvez le voir sur l'image, la boucle for
commence par *p
Où p
pointe le str
. À ce stade, *p
A S
.
En bouclant en continu le for
, il atteint finalement str[9]
Qui a '\0'
Ce qui signifie NULL
.
À ce stade, l'instruction de condition *p
Dans for (p = str; *p; p++)
est égale à NULL
, de sorte que le code se détachera de la boucle for
.
Il s'agit de la condition de la boucle.
Si cette condition n'est pas remplie, la boucle n'est plus exécutée.*p
déréférence le pointeur p
et renvoie le caractère pointé dans la chaîne str
.
La chaîne de style C str
se termine par la valeur \0
.
La boucle parcourt chaque caractère (en utilisant p
) jusqu'à ce que la condition ne soit pas remplie.
En C, une valeur de 0
ou \0
est comme la signification de false
, c'est-à-dire que la condition n'est pas remplie.
Toute autre valeur est similaire à la signification de true
, c'est-à-dire que la condition est remplie.
En bref, p
itère sur chaque caractère dans str
et s'arrête dès qu'il atteint le caractère de terminaison de chaîne \0
.
Pourquoi ne pas utiliser p
au lieu de *p
?
Parce que p
est un pointeur et contient une adresse. Il est parfois difficile, voire impossible, d'utiliser uniquement l'arithmétique des adresses. Ce n'est pas une bonne pratique et rend le code difficile à lire.*p
est le pointeur déréférencé et contient la valeur vers laquelle p
pointe. Dans ce cas, il est facile d'utiliser les valeurs vers lesquelles p
pointe, car vous savez que la chaîne se termine par un \0
. Comme condition (if
, while
, etc.) *p
est équivalent à *p != '\0'
.
Tout d'abord, vous devez saisir un concept de pointeur, comme son nom l'indique. Le pointeur contient l'adresse de la variable.
int var=0;
int *p;
int p=&var;
dans ce code p
est un pointeur et printf("%d",p);
affiche l'adresse de la variable var
et printf("%d",*p);
affiche la valeur de la variable var
qui dans cet exemple est 0.
Deuxièmement, vous devez comprendre le fonctionnement des tableaux. Les tableaux sont une sorte de structure de données qui peut stocker une taille fixe [~ # ~] séquentielle [~ # ~] collection d'éléments du même type.
int array[3]={9,8,7};
printf("%d",array[0]); //prints what is on 1st position,9
printf("%d",array[1]); //prints what is on 2nd position,8
printf("%d",array[2]); //prints what is on 3rd position,7
les opérateurs []
ne sont qu'un travail convivial avec les tableaux. Les trois dernières lignes de code peuvent être remplacées par les lignes suivantes (et elles feront de même):
printf("%d",*(array+0)); //prints what is on 1st position,9
printf("%d",*(array+1)); //prints what is on 2nd position,8
printf("%d",*(array+2)); //prints what is on 3rd position,7
array
est le pointeur sur le premier élément du tableau (contient l'adresse du premier élément du tableau), donc en le déréférençant, nous obtenons la valeur du premier élément, par exemple *array
. Nous savons que les tableaux sont séquentiels ce qui signifie que array+1
Pointe vers le deuxième élément du tableau, donc en déréférençant cela, vous obtenez la valeur du deuxième élément, par exemple *(array+1)
, etc.
Il en va de même pour les chaînes car elles sont un tableau de caractères, sauf que la chaîne a "\ 0" (caractère nul) à la fin des chaînes.
char str[128] = "Some Text";
char *p;
for (p = str; *p; p++)
{
printf("%c",*p);
}
Ce programme imprime la chaîne str
.
p = str
// attribue l'adresse du premier caractère de la chaîne str
à p
, nous ne perdrons pas une piste du premier caractère de la chaîne, nous utilisons donc p
pas str
pour itérer
*p
// cette expression signifie *p!=0
Donc c'est vrai jusqu'à ce que vous arriviez à la fin de la chaîne, rappelez-vous que '0' en ascii a une valeur entière 48
p++
// à la fin du bloc pour vous ajoutez +1 à p
pour obtenir l'adresse du prochain caractère
Cela peut s'expliquer comme suit:
for( initialization ; Conditional Expression ; expression3)
{
Code here will execute while 2nd Expression(Conditional Expression) is true
true means non-zero value
'\0' is equivelant to 0,so when *p equal '\0' : loop will terminate
}