J'ai eu le plaisir récent d'expliquer des pointeurs à un débutant en programmation C et je suis tombé sur la difficulté suivante. Cela peut ne pas sembler un problème du tout si vous savez déjà comment utiliser les pointeurs, mais essayez de regarder l'exemple suivant avec un esprit clair:
int foo = 1;
int *bar = &foo;
printf("%p\n", (void *)&foo);
printf("%i\n", *bar);
Pour le débutant absolu, le résultat peut être surprenant. À la ligne 2, il venait de déclarer que * bar était & foo, mais à la ligne 4, il s'avère que * bar est en fait foo au lieu de & foo!
La confusion, pourrait-on dire, provient de l'ambiguïté du symbole *: à la ligne 2, il est utilisé pour déclarer un pointeur. À la ligne 4, il est utilisé comme un opérateur unaire qui récupère la valeur sur laquelle pointe le pointeur. Deux choses différentes, non?
Cependant, cette «explication» n'aide pas du tout un débutant. Il introduit un nouveau concept en signalant une différence subtile. Cela ne peut pas être la bonne façon de l'enseigner.
Alors, comment Kernighan et Ritchie l'ont-ils expliqué?
L'opérateur unaire * est l'opérateur d'indirection ou de déréférencement; lorsqu'il est appliqué à un pointeur, il accède à l'objet vers lequel pointe le pointeur. […]
La déclaration du pointeur ip,
int *ip
est conçue comme un mnémonique; il dit que l'expression*ip
est un int. La syntaxe de la déclaration d'une variable imite la syntaxe des expressions dans lesquelles la variable peut apparaître .
int *ip
devrait être lu comme " *ip
retournera un int
"? Mais pourquoi alors l'affectation après la déclaration ne suit-elle pas ce modèle? Que faire si un débutant souhaite initialiser la variable? int *ip = 1
(lire: *ip
renverra un int
et le int
est 1
) ne fonctionnera pas comme prévu. Le modèle conceptuel ne semble tout simplement pas cohérent. Est-ce que j'ai râté quelque chose?
Edit: Il a essayé de résumer les réponses ici .
*
dans une déclaration il y a un jeton signifiant "déclarer un pointeur", dans les expressions c'est l'opérateur de déréférencement, et que ces deux représentent des choses différentes qui se trouvent avoir le même symbole (identique à l'opérateur de multiplication - même symbole, signification différente). C'est déroutant, mais tout ce qui est différent de l'état actuel des choses va être encore pire.int* bar
rend plus évident que l'étoile fait en fait partie du type, pas de l'identifiant. Bien sûr, cela vous pose différents problèmes avec des trucs peu intuitifs commeint* a, b
.*
peut avoir deux significations différentes selon le contexte. Tout comme la même lettre peut être prononcée différemment selon le mot dans lequel elle se trouve, il est donc difficile d'apprendre à parler plusieurs langues. Si chaque concept / opération avait son propre symbole, nous aurions besoin de claviers beaucoup plus grands, donc les symboles sont recyclés quand cela a du sens de le faire.int* p
), tout en avertissant votre élève de ne pas utiliser plusieurs déclarations dans la même ligne lorsque des pointeurs sont impliqués. Lorsque l'élève a complètement compris le concept des pointeurs, expliquez à l'élève que laint *p
syntaxe est est équivalente, puis expliquez le problème avec plusieurs déclarations.Réponses:
Pour que votre élève comprenne la signification du
*
symbole dans différents contextes, il doit d'abord comprendre que les contextes sont effectivement différents. Une fois qu'ils comprennent que les contextes sont différents (c'est-à-dire la différence entre le côté gauche d'un devoir et une expression générale), il n'est pas trop difficile de comprendre les différences.Expliquez d'abord que la déclaration d'une variable ne peut pas contenir d'opérateurs (démontrez-le en montrant que placer un symbole
-
ou+
dans une déclaration de variable provoque simplement une erreur). Ensuite, montrez qu'une expression (c'est-à-dire sur le côté droit d'une affectation) peut contenir des opérateurs. Assurez-vous que l'élève comprend qu'une expression et une déclaration de variable sont deux contextes complètement différents.Lorsqu'ils comprennent que les contextes sont différents, vous pouvez continuer en expliquant que lorsque le
*
symbole est dans une déclaration de variable devant l'identificateur de variable, cela signifie «déclarer cette variable comme un pointeur». Ensuite, vous pouvez expliquer que lorsqu'il est utilisé dans une expression (en tant qu'opérateur unaire), le*
symbole est «l'opérateur de déréférencement» et il signifie «la valeur à l'adresse de» plutôt que sa signification antérieure.Pour vraiment convaincre votre élève, expliquez que les créateurs de C auraient pu utiliser n'importe quel symbole pour désigner l'opérateur de déréférencement (c'est-à-dire qu'ils auraient pu utiliser à la
@
place), mais pour une raison quelconque, ils ont pris la décision de conception d'utiliser*
.Dans l'ensemble, il n'y a aucun moyen d'expliquer que les contextes sont différents. Si l'élève ne comprend pas que les contextes sont différents, il ne peut pas comprendre pourquoi le
*
symbole peut signifier différentes choses.la source
La raison pour laquelle la sténographie:
dans votre exemple peut être déroutant, c'est qu'il est facile de mal interpréter cela comme étant équivalent à:
quand cela signifie en fait:
Écrit comme ceci, avec la déclaration de variable et l'affectation séparées, il n'y a pas de tel potentiel de confusion, et l'utilisation du parallélisme de déclaration ↔ décrit dans votre devis K&R fonctionne parfaitement:
La première ligne déclare une variable
bar
, comme*bar
unint
.La deuxième ligne attribue l'adresse de
foo
àbar
, faisant de*bar
(anint
) un alias pourfoo
(également unint
).Lors de l'introduction de la syntaxe du pointeur C aux débutants, il peut être utile de s'en tenir initialement à ce style de séparation des déclarations de pointeur des affectations, et d'introduire uniquement la syntaxe abrégée combinée (avec des avertissements appropriés sur son potentiel de confusion) une fois que les concepts de base de l'utilisation du pointeur dans C ont été correctement internalisés.
la source
typedef
.typedef int *p_int;
signifie qu'une variable de typep_int
a la propriété qui*p_int
est unint
. Ensuite, nous avonsp_int bar = &foo;
. Encourager quiconque à créer des données non initialisées et à les attribuer plus tard comme une habitude par défaut semble ... être une mauvaise idée.int a[2] = {47,11};
que ce n'est pas une initialisation de l'élément (inexistant)a[2]
eiher.*
devrait faire partie du type, non lié à la variable, et vous pourrez alors écrireint* foo_ptr, bar_ptr
pour déclarer deux pointeurs. Mais il déclare en fait un pointeur et un entier.À court de déclarations
Il est bon de connaître la différence entre la déclaration et l'initialisation. Nous déclarons les variables en tant que types et les initialisons avec des valeurs. Si nous faisons les deux en même temps, nous l'appelons souvent une définition.
1.
int a; a = 42;
Nous déclarons un
int
nommé a . Puis on l'initialise en lui donnant une valeur42
.2.
int a = 42;
Nous déclarons et
int
nommons a et lui donnons la valeur 42. Il est initialisé avec42
. Une définition.3.
a = 43;
Lorsque nous utilisons les variables, nous disons que nous opérons sur elles.
a = 43
est une opération d'affectation. Nous attribuons le nombre 43 à la variable a.En disant
nous déclarons que bar est un pointeur vers un int. En disant
nous déclarons bar et l'initialisons avec l'adresse de foo .
Après avoir initialisé la barre, nous pouvons utiliser le même opérateur, l'astérisque, pour accéder à la valeur de foo et l'utiliser . Sans l'opérateur, nous accédons et opérons sur l'adresse vers laquelle pointe le pointeur.
En plus de cela, je laisse parler l'image.
Quoi
Une ASCIIMATION simplifiée sur ce qui se passe. (Et voici une version player si vous souhaitez mettre en pause etc.)
la source
La deuxième déclaration
int *bar = &foo;
peut être visualisée en images dans la mémoire comme,Voici maintenant
bar
un pointeur de typeint
contenant l'adresse&
defoo
. En utilisant l'opérateur unaire,*
nous déférons pour récupérer la valeur contenue dans 'foo' en utilisant le pointeurbar
.EDIT : Mon approche avec les débutants est d'expliquer le
memory address
d'une variable ieMemory Address:
Chaque variable est associée à une adresse fournie par le système d'exploitation. Dansint a;
,&a
est l'adresse de la variablea
.Continuez à expliquer les types de variables de base dans
C
as,Types of variables:
Les variables peuvent contenir des valeurs de types respectifs mais pas des adresses.Introducing pointers:
Comme indiqué ci-dessus, les variables, par exempleIl est possible d'assigner
b = a
mais pasb = &a
, car la variableb
peut contenir une valeur mais pas une adresse, nous avons donc besoin de pointeurs .Pointer or Pointer variables :
Si une variable contient une adresse, elle est appelée variable de pointeur. Utilisez*
dans la déclaration pour indiquer qu'il s'agit d'un pointeur.la source
int *ip
comme "ip est un pointeur (*) de type int" vous avez des ennuis en lisant quelque chose commex = (int) *ip
.x = (int) *ip;
, obtenez la valeur en déréférençant le pointeurip
et convertissez la valeur enint
quel que soit le typeip
.int* bar = &foo;
fait des charges plus de sens. Oui, je sais que cela pose des problèmes lorsque vous déclarez plusieurs pointeurs dans une seule déclaration. Non, je ne pense pas que cela compte du tout.En regardant les réponses et les commentaires ici, il semble y avoir un accord général sur le fait que la syntaxe en question peut être déroutante pour un débutant. La plupart d'entre eux proposent quelque chose dans ce sens:
Vous pouvez écrire
int* bar
au lieu deint *bar
pour souligner la différence. Cela signifie que vous ne suivrez pas l'approche K&R "déclaration mimics use", mais l' approche Stroustrup C ++ :Nous ne déclarons
*bar
pas être un entier. Nous déclaronsbar
être unint*
. Si nous voulons initialiser une variable nouvellement créée dans la même ligne, il est clair que nous avons affaire àbar
, non*bar
.int* bar = &foo;
Les inconvénients:
int* foo, bar
vsint *foo, *bar
).Edit: Une approche différente qui a été suggérée, est de suivre la voie «mimique» de K&R, mais sans la syntaxe «abrégée» (voir ici ). Dès que vous omettez de faire une déclaration et une affectation dans la même ligne , tout paraîtra beaucoup plus cohérent.
Cependant, tôt ou tard, l'étudiant devra traiter les pointeurs comme arguments de fonction. Et des pointeurs comme types de retour. Et des pointeurs vers des fonctions. Vous devrez expliquer la différence entre
int *func();
etint (*func)();
. Je pense que tôt ou tard, les choses s'effondreront. Et peut-être que plus tôt est mieux que plus tard.la source
Il y a une raison pour laquelle le style K&R favorise
int *p
et le style Stroustrupint* p
; les deux sont valides (et signifient la même chose) dans chaque langue, mais comme Stroustrup l'a dit:Maintenant, puisque vous essayez d'enseigner C ici, cela suggère que vous devriez mettre davantage l'accent sur les expressions que sur les types, mais certaines personnes peuvent plus facilement mettre l'accent sur un accent plus rapidement que sur l'autre, et c'est à propos d'eux plutôt que du langage.
Par conséquent, certaines personnes trouveront plus facile de partir de l'idée que un
int*
est une chose différente de unint
et partir de là.Si quelqu'un ne Grok rapidement la façon de voir ce que les utilisations
int* bar
d'avoirbar
comme une chose qui n'est pas un int, mais un pointeur versint
, puis ils vont voir rapidement que*bar
est en train de faire quelque chose àbar
, et le reste suivra. Une fois que vous avez fait cela, vous pouvez expliquer plus tard pourquoi les codeurs C ont tendance à préférerint *bar
.Ou pas. S'il y avait une façon dont tout le monde avait compris le concept pour la première fois, vous n'auriez eu aucun problème en premier lieu, et la meilleure façon de l'expliquer à une personne ne serait pas nécessairement la meilleure façon de l'expliquer à une autre.
la source
int* p = &a
nous pouvons le faireint* r = *p
. Je suis à peu près sûr qu'il l'a couvert dans La conception et l'évolution de C ++ , mais cela fait longtemps que je n'ai pas lu cela, et j'ai bêtement prêté ma copie à quelqu'un.int& r = *p
. Et je parie que l'emprunteur essaie toujours de digérer le livre.Var A, B: ^Integer;
indique clairement que le type "pointeur vers entier" s'applique à la fois àA
etB
. L'utilisation d'unK&R
styleint *a, *b
est également réalisable; mais une déclaration commeint* a,b;
, cependant, a l'air d'êtrea
etb
sont toutes les deux déclarées commeint*
, mais en réalité, elle déclarea
comme unint*
etb
comme unint
.tl; dr:
R: ne le faites pas. Expliquez les pointeurs au débutant et montrez-leur comment représenter leurs concepts de pointeurs dans la syntaxe C après.
IMO, la syntaxe C n'est pas terrible, mais n'est pas non plus merveilleuse: ce n'est ni un grand obstacle si vous comprenez déjà les pointeurs, ni aucune aide pour les apprendre.
Par conséquent: commencez par expliquer les pointeurs et assurez-vous qu'ils les comprennent vraiment:
Expliquez-les avec des diagrammes encadrés et flèches. Vous pouvez le faire sans adresses hexadécimales, si elles ne sont pas pertinentes, affichez simplement les flèches pointant soit vers une autre boîte, soit vers un symbole nul.
Expliquez avec un pseudo-code: écrivez simplement l' adresse de foo et la valeur stockée dans bar .
Ensuite, lorsque votre novice comprend ce que sont les pointeurs, pourquoi et comment les utiliser; puis montrez le mappage sur la syntaxe C.
Je soupçonne que la raison pour laquelle le texte K&R ne fournit pas de modèle conceptuel est qu'ils comprenaient déjà les pointeurs et supposaient probablement que tous les autres programmeurs compétents à l'époque le faisaient aussi. Le mnémonique n'est qu'un rappel de la mise en correspondance du concept bien compris à la syntaxe.
la source
Ce problème est quelque peu déroutant lorsque vous commencez à apprendre C.
Voici les principes de base qui pourraient vous aider à démarrer:
Il n'y a que quelques types de base en C:
char
: une valeur entière de 1 octet.short
: une valeur entière de 2 octets.long
: une valeur entière de 4 octets.long long
: une valeur entière de 8 octets.float
: une valeur non entière avec la taille de 4 octets.double
: une valeur non entière de 8 octets.Notez que la taille de chaque type est généralement définie par le compilateur et non par le standard.
Les types entiers
short
,long
etlong long
sont généralement suiviesint
.Cependant, ce n'est pas une obligation et vous pouvez les utiliser sans l'extension
int
.Alternativement, vous pouvez simplement énoncer
int
, mais cela pourrait être interprété différemment par différents compilateurs.Donc pour résumer ceci:
short
est identiqueshort int
mais pas nécessairement identique àint
.long
est identiquelong int
mais pas nécessairement identique àint
.long long
est identiquelong long int
mais pas nécessairement identique àint
.Sur un compilateur donné,
int
est soitshort int
oulong int
soitlong long int
.Si vous déclarez une variable d'un certain type, vous pouvez également déclarer une autre variable pointant vers elle.
Par exemple:
int a;
int* b = &a;
Donc, en substance, pour chaque type de base, nous avons également un type de pointeur correspondant.
Par exemple:
short
etshort*
.Il y a deux façons de "regarder" la variable
b
(c'est probablement ce qui déroute la plupart des débutants) :Vous pouvez considérer
b
comme une variable de typeint*
.Vous pouvez considérer
*b
comme une variable de typeint
.Par conséquent, certaines personnes déclarent
int* b
, tandis que d'autres déclarentint *b
.Mais le fait est que ces deux déclarations sont identiques (les espaces n'ont pas de sens).
Vous pouvez utiliser soit
b
comme pointeur vers une valeur entière, soit*b
comme valeur entière pointée réelle.Vous pouvez obtenir (lire) la valeur pointue:
int c = *b
.Et vous pouvez définir (écrire) la valeur pointue:
*b = 5
.Un pointeur peut pointer vers n'importe quelle adresse mémoire, et pas seulement vers l'adresse d'une variable que vous avez précédemment déclarée. Cependant, vous devez être prudent lorsque vous utilisez des pointeurs afin d'obtenir ou de définir la valeur située à l'adresse mémoire pointée.
Par exemple:
int* a = (int*)0x8000000;
Ici, nous avons une variable
a
pointant vers l'adresse mémoire 0x8000000.Si cette adresse mémoire n'est pas mappée dans l'espace mémoire de votre programme, toute opération de lecture ou d'écriture à l'aide
*a
entraînera probablement le blocage de votre programme, en raison d'une violation d'accès à la mémoire.Vous pouvez modifier en toute sécurité la valeur de
a
, mais vous devez faire très attention en modifiant la valeur de*a
.Le type
void*
est exceptionnel dans le fait qu'il n'a pas de «type valeur» correspondant qui peut être utilisé (c'est-à-dire que vous ne pouvez pas déclarervoid a
). Ce type est utilisé uniquement comme un pointeur général vers une adresse mémoire, sans spécifier le type de données qui réside dans cette adresse.la source
Peut-être que le parcourir un peu plus facilite les choses:
Demandez-leur de vous dire ce qu'ils attendent de la sortie sur chaque ligne, puis demandez-leur d'exécuter le programme et de voir ce qui apparaît. Expliquez leurs questions (la version nue qui s'y trouve en suscitera certainement quelques-unes - mais vous pourrez vous soucier du style, de la rigueur et de la portabilité plus tard). Ensuite, avant que leur esprit ne se transforme en bouillie après avoir trop réfléchi ou qu'ils ne deviennent un zombie après le déjeuner, écrivez une fonction qui prend une valeur et la même qui prend un pointeur.
D'après mon expérience, il surmonte ce "pourquoi est-ce que cela s'imprime de cette façon?" bosse, puis montrant immédiatement pourquoi cela est utile dans les paramètres de fonction en jouant sur le terrain (en prélude à certains éléments de base de K&R comme l'analyse de chaînes / traitement de tableau) qui rend la leçon non seulement logique, mais collante.
La prochaine étape consiste à les amener à vous expliquer comment se
i[0]
rapporte à&i
. S'ils peuvent le faire, ils ne l'oublieront pas et vous pourrez commencer à parler de structures, même un peu à l'avance, juste pour que cela pénètre.Les recommandations ci-dessus sur les boîtes et les flèches sont également bonnes, mais elles peuvent également aboutir à une discussion approfondie sur le fonctionnement de la mémoire - ce qui est un discours qui doit avoir lieu à un moment donné, mais peut détourner l'attention du point immédiatement à portée de main. : comment interpréter la notation du pointeur en C.
la source
int foo = 1;
. Maintenant , c'est OK:int *bar; *bar = foo;
. Ce n'est pas OK:int *bar = foo;
Le type de l' expression
*bar
estint
; ainsi, le type de la variable (et de l'expression)bar
estint *
. Étant donné que la variable a un type pointeur, son initialiseur doit également avoir un type pointeur.Il existe une incohérence entre l'initialisation et l'affectation des variables de pointeur; c'est juste quelque chose qui doit être appris à la dure.
la source
Je préfère le lire comme le premier
*
s'applique àint
plus debar
.la source
int* a, b
ne fait pas ce qu'ils pensent faire.int* a,b
devrait être utilisé du tout. Pour une meilleure lisibilité, mise à jour, etc ... il ne devrait y avoir qu'une seule déclaration de variable par ligne et jamais plus. C'est quelque chose à expliquer aux débutants aussi, même si le compilateur peut le gérer.*
comme faisant partie du type et de simplement découragerint* a, b
. Sauf si vous préférez dire que*a
c'est de typeint
plutôt que dea
pointer versint
...int *a, b;
ne devrait pas être utilisé. Déclarer deux variables avec des types différents dans la même instruction est une pratique plutôt médiocre et un bon candidat pour les problèmes de maintenance sur toute la ligne. C'est peut-être différent pour ceux d'entre nous qui travaillent dans le domaine embarqué où unint*
et unint
sont souvent de tailles différentes et parfois stockés dans des emplacements de mémoire complètement différents. C'est l'un des nombreux aspects du langage C qui serait le mieux enseigné car «c'est permis, mais ne le faites pas».Question 1
: Qu'est-ce que c'estbar
?Ans
: C'est une variable pointeur (à taperint
). Un pointeur doit pointer vers un emplacement mémoire valide et plus tard, il doit être déréférencé (* bar) à l'aide d'un opérateur unaire*
afin de lire la valeur stockée à cet emplacement.Question 2
: Quel est&foo
?Ans
: foo est une variable de typeint
.qui est stockée dans un emplacement mémoire valide et cet emplacement nous l'obtenons de l'opérateur&
alors maintenant ce que nous avons est un emplacement mémoire valide&foo
.Donc, les deux sont réunis, c'est-à-dire que le pointeur avait besoin d'un emplacement mémoire valide et qui est obtenu par
&foo
l'initialisation est bonne.Maintenant, le pointeur
bar
pointe vers un emplacement de mémoire valide et la valeur qui y est stockée peut être obtenue en le déréférençant, c'est-à-dire*bar
la source
Vous devez signaler un débutant qui * a une signification différente dans la déclaration et dans l'expression. Comme vous le savez, * dans l'expression est un opérateur unaire, et * dans la déclaration n'est pas un opérateur et juste une sorte de syntaxe se combinant avec le type pour faire savoir au compilateur qu'il s'agit d'un type pointeur. il est préférable de dire un débutant, "* a une signification différente. Pour comprendre la signification de *, vous devriez trouver où * est utilisé"
la source
Je pense que le diable est dans l'espace.
J'écrirais (non seulement pour le débutant, mais aussi pour moi): int * bar = & foo; au lieu de int * bar = & foo;
cela devrait mettre en évidence la relation entre la syntaxe et la sémantique
la source
Il a déjà été noté que * a plusieurs rôles.
Il existe une autre idée simple qui peut aider un débutant à comprendre les choses:
Pensez que "=" a également plusieurs rôles.
Lorsque l'affectation est utilisée sur la même ligne avec la déclaration, considérez-la comme un appel au constructeur et non comme une affectation arbitraire.
Quand tu vois:
Pensez que c'est presque équivalent à:
Les parenthèses ont préséance sur l'astérisque, ainsi "& foo" est beaucoup plus facilement attribué intuitivement à "bar" plutôt qu'à "* bar".
la source
J'ai vu cette question il y a quelques jours, puis j'ai lu l'explication de la déclaration de type de Go sur le blog Go . Il commence par donner un compte rendu des déclarations de type C, ce qui semble être une ressource utile à ajouter à ce fil, même si je pense qu'il y a déjà des réponses plus complètes.
(Il décrit ensuite comment étendre cette compréhension aux pointeurs de fonction, etc.)
C'est une façon à laquelle je n'y ai pas pensé auparavant, mais cela semble être un moyen assez simple de tenir compte de la surcharge de la syntaxe.
la source
Si le problème est la syntaxe, il peut être utile d'afficher un code équivalent avec template / using.
Cela peut ensuite être utilisé comme
Après cela, comparez la syntaxe normale / C avec cette approche C ++ uniquement. Ceci est également utile pour expliquer les pointeurs const.
la source
La source de confusion vient du fait que le
*
symbole peut avoir des significations différentes en C, selon le fait dans lequel il est utilisé. Pour expliquer le pointeur à un débutant, la signification de*
symbole dans un contexte différent doit être expliquée.Dans la déclaration
le
*
symbole n'est pas l'opérateur d'indirection . Au lieu de cela, cela aide à spécifier le type d'bar
information du compilateur quibar
est un pointeur versint
un fichier . En revanche, lorsqu'il apparaît dans une instruction, le*
symbole (lorsqu'il est utilisé comme opérateur unaire ) effectue une indirection. Par conséquent, la déclarationserait erroné car il attribue l'adresse de
foo
à l'objet quibar
pointe vers, pas àbar
lui-même.la source
"peut-être que l'écrire comme int * bar rend plus évident que l'étoile fait en fait partie du type, pas de l'identifiant." Moi aussi. Et je dis que c'est un peu comme Type, mais seulement pour un seul nom de pointeur.
"Bien sûr, cela vous pose différents problèmes avec des trucs peu intuitifs comme int * a, b."
la source
Ici, vous devez utiliser, comprendre et expliquer la logique du compilateur, pas la logique humaine (je sais, vous êtes un humain, mais ici vous devez imiter l'ordinateur ...).
Quand tu écris
le compilateur regroupe comme
Autrement dit: voici une nouvelle variable, son nom est
bar
, son type est un pointeur sur int et sa valeur initiale est&foo
.Et vous devez ajouter: les
=
ci - dessus indique une initialisation pas afféterie, alors que dans les expressions suivantes ,*bar = 2;
il est un afféterieModifier par commentaire:
Attention: en cas de déclaration multiple, le
*
n'est lié qu'à la variable suivante:bar est un pointeur vers int initialisé par l'adresse de foo, b est un int initialisé à 2, et dans
bar en pointeur fixe vers int, et p est un pointeur vers un pointeur vers un int initialisé à l'adresse ou à la barre.
la source
int* a, b;
déclare a comme pointeur vers anint
, mais b comme anint
. Le*
symbole a simplement deux significations distinctes: dans une déclaration, il indique un type de pointeur et dans une expression, il s'agit de l'opérateur de déréférence unaire.*
in rattacha au type, de sorte que le pointeur soit initialisé alors que dans une affectaction la valeur pointée est affectée. Mais au moins tu m'as donné un joli chapeau :-)Fondamentalement, le pointeur n'est pas une indication de tableau. Le débutant pense facilement que le pointeur ressemble à un tableau. la plupart des exemples de chaînes utilisant le
"char * pstr" il ressemble à
"char str [80]"
Mais, choses importantes, le pointeur est traité comme un simple entier dans le niveau inférieur du compilateur.
Regardons des exemples:
Les résultats seront comme ceci 0x2a6b7ed0 est l'adresse de str []
Donc, fondamentalement, gardez à l'esprit que le pointeur est une sorte d'entier. présentant l'adresse.
la source
J'expliquerais que les ints sont des objets, tout comme les flottants, etc. Un pointeur est un type d'objet dont la valeur représente une adresse en mémoire (d'où la valeur par défaut d'un pointeur NULL).
Lorsque vous déclarez un pointeur pour la première fois, vous utilisez la syntaxe type-pointer-name. Il est lu comme un "nom de pointeur entier appelé qui peut pointer vers l'adresse de n'importe quel objet entier". Nous n'utilisons cette syntaxe que lors de la déclinaison, de la même manière que nous déclarons un int comme «int num1», mais nous n'utilisons «num1» que lorsque nous voulons utiliser cette variable, et non «int num1».
int x = 5; // un objet entier avec une valeur de 5
int * ptr; // un entier avec une valeur NULL par défaut
Pour faire pointer un pointeur sur l'adresse d'un objet, nous utilisons le symbole '&' qui peut être lu comme "l'adresse de".
ptr = & x; // maintenant la valeur est l'adresse de 'x'
Comme le pointeur n'est que l'adresse de l'objet, pour obtenir la valeur réelle conservée à cette adresse, nous devons utiliser le symbole '*' qui, lorsqu'il est utilisé avant un pointeur, signifie "la valeur à l'adresse pointée par".
std :: cout << * ptr; // affiche la valeur à l'adresse
Vous pouvez expliquer brièvement que « » est un «opérateur» qui renvoie différents résultats avec différents types d'objets. Lorsqu'il est utilisé avec un pointeur, l' opérateur « » ne signifie plus «multiplié par».
Il est utile de dessiner un diagramme montrant comment une variable a un nom et une valeur et un pointeur a une adresse (le nom) et une valeur et montrer que la valeur du pointeur sera l'adresse de l'int.
la source
Un pointeur n'est qu'une variable utilisée pour stocker des adresses.
La mémoire d'un ordinateur est composée d'octets (un octet se compose de 8 bits) disposés de manière séquentielle. Chaque octet a un nombre qui lui est associé, tout comme l'index ou l'indice dans un tableau, qui est appelé l'adresse de l'octet. L'adresse de l'octet commence de 0 à un de moins que la taille de la mémoire. Par exemple, disons dans un 64 Mo de RAM, il y a 64 * 2 ^ 20 = 67108864 octets. L'adresse de ces octets débutera donc de 0 à 67108863.
Voyons ce qui se passe lorsque vous déclarez une variable.
marques int;
Comme nous le savons, un int occupe 4 octets de données (en supposant que nous utilisons un compilateur 32 bits), le compilateur réserve donc 4 octets consécutifs de la mémoire pour stocker une valeur entière. L'adresse du premier octet des 4 octets alloués est appelée adresse des marques de variable. Disons que l'adresse de 4 octets consécutifs est 5004, 5005, 5006 et 5007, alors l'adresse des marques variables sera 5004.
Déclaration des variables de pointeur
Comme déjà dit, un pointeur est une variable qui stocke une adresse mémoire. Comme toute autre variable, vous devez d'abord déclarer une variable de pointeur avant de pouvoir l'utiliser. Voici comment déclarer une variable de pointeur.
Syntaxe:
data_type *pointer_name;
data_type est le type du pointeur (également appelé type de base du pointeur). nom_pointer est le nom de la variable, qui peut être n'importe quel identifiant C valide.
Prenons quelques exemples:
int * ip signifie que ip est une variable pointeur capable de pointer vers des variables de type int. En d'autres termes, une variable de pointeur ip peut stocker l'adresse des variables de type int uniquement. De même, la variable pointeur fp ne peut stocker que l'adresse d'une variable de type float. Le type de variable (également appelé type de base) ip est un pointeur vers int et le type de fp est un pointeur vers float. Une variable pointeur de type pointeur vers int peut être représentée symboliquement par (int *). De même, une variable pointeur de type pointeur vers float peut être représentée par (float *)
Après avoir déclaré une variable de pointeur, l'étape suivante consiste à lui attribuer une adresse mémoire valide. Vous ne devez jamais utiliser une variable de pointeur sans lui attribuer une adresse mémoire valide, car juste après la déclaration, elle contient une valeur de garbage et peut pointer vers n'importe où dans la mémoire. L'utilisation d'un pointeur non attribué peut donner un résultat imprévisible. Cela peut même provoquer le blocage du programme.
Source: thecguru est de loin l'explication la plus simple mais détaillée que j'aie jamais trouvée.
la source