Récemment, j'ai essayé d'expliquer les pointeurs de manière visuelle, comme des flashcards.
Question 001: Ceci est le dessin d'un emplacement dans la mémoire de l'ordinateur. Est-il vrai que son adresse est
0x23452
? Pourquoi?Réponse: Oui, car
0x23452
décrit où l'ordinateur peut trouver cet emplacement.
Question 002: Est-il vrai que le caractère
b
est stocké à l'intérieur de l'emplacement mémoire0x23452
? Pourquoi?Réponse: Non, car le caractère
a
est en fait stocké à l'intérieur.
Question 003: Est-il vrai qu'un pointeur est stocké à l'intérieur de l'emplacement mémoire
0x23452
? Pourquoi?Réponse: Oui, car l'adresse de l'emplacement mémoire
0x34501
est stocké à l'intérieur.
Question 004: Est-il vrai qu'un pointeur est stocké à l'intérieur de l'emplacement mémoire
0x23452
? Pourquoi?Réponse: Oui, car l'adresse d'un autre emplacement mémoire y est stockée.
Maintenant pour la partie qui m'inquiète. Un ingénieur logiciel m'a expliqué des pointeurs comme ceci:
Un pointeur est une variable dont la valeur est l'adresse mémoire d'une autre variable.
Sur la base des quatre cartes mémoire que je vous ai toutes montrées, je définirais les pointeurs d'une manière légèrement différente:
Un pointeur est un emplacement mémoire dont la valeur est l'adresse mémoire d'un autre emplacement mémoire.
Est-il sûr de dire qu'une variable est la même chose qu'un emplacement mémoire?
Sinon, qui a raison? Quelle est la différence entre une variable et un emplacement mémoire?
Une variable est une construction logique qui va dans le sens d'un algorithme, tandis qu'un emplacement de mémoire est une construction physique qui décrit le fonctionnement d'un ordinateur. De manière générale, pour exécuter un programme, il y a un mappage (généré par le compilateur) entre la notion logique de variable et le stockage de l'ordinateur.
(Même dans le langage d'assemblage, nous avons une notion de variables (logiques) allant vers l'algorithme et l'intention, et des emplacements de mémoire (physiques), bien qu'ils soient plus confondus dans l'assemblage.)
Une variable est un concept de haut (er) niveau. Une variable représente soit une inconnue (comme en mathématiques ou affectation de programmation) ou un espace réservé qui peut être remplacé par une valeur (comme en programmation: paramètres).
Un emplacement de mémoire est un concept de bas (er) niveau. Un emplacement mémoire peut être utilisé pour stocker une valeur, parfois, pour stocker la valeur d'une variable. Cependant, un registre CPU est une autre façon de stocker la valeur de certaines variables. Les registres CPU sont également des emplacements de stockage de bas niveau (er), mais ils ne sont pas des emplacements de mémoire car ils n'ont pas d'adresse, juste des noms.
Dans un certain sens, une variable est un mécanisme d'abstraction pour exprimer l'intention du programme, tandis qu'un emplacement de mémoire est une entité physique de l'environnement de traitement qui fournit le stockage et la récupération.
Question 003: Est-il vrai qu'un pointeur est stocké à l'intérieur de l'emplacement de mémoire 0x23452? Pourquoi?
Nous ne pouvons pas dire à l'avance. Ce n'est pas parce qu'il y a une valeur qui fonctionnerait comme une adresse que cela signifie que c'est cette adresse, ce pourrait être l'entier (décimal) 144466. Nous ne pouvons pas faire d'hypothèses sur l'interprétation des valeurs simplement en fonction de leur apparence numérique.
Question 004: Est-il vrai qu'un pointeur est stocké à l'intérieur de l'emplacement de mémoire 0x23452? Pourquoi?
C'est en effet une question étrange. Ils s'attendent à certaines hypothèses basées sur les cases, cependant, notons que les adresses augmentent de 1 pour chaque case. Dans tout ordinateur moderne, cela signifierait que chaque boîte peut contenir une adressabilité octet par octet qui est la norme depuis des décennies. Cependant, un octet ne fait que 8 bits et peut aller de 0 à 255 (pour les valeurs non signées); Pourtant, ils montrent une valeur beaucoup plus grande stockée dans l'une de ces adresses, donc très suspecte. (Cela pourrait fonctionner s'il s'agissait d'une machine adressée par Word, mais cela ne dit pas cela, et peu de machines le sont aujourd'hui, bien que certaines machines éducatives le soient.)
Sur la base des quatre cartes mémoire que je vous ai toutes montrées, je définirais les pointeurs d'une manière légèrement différente:
Un pointeur est un emplacement mémoire dont la valeur est l'adresse mémoire d'un autre emplacement mémoire.
Bien qu'il existe des situations où cette réflexion est correcte, vous mélangez des métaphores ici. La notion de variable va à l'algorithme et à son intention - il n'est pas nécessaire de supposer que toutes les variables ont des emplacements de mémoire. Certaines variables (en particulier les tableaux) ont des emplacements mémoire car les emplacements mémoire prennent en charge l'adressage (alors que les registres CPU ne peuvent être nommés que non indexés).
Pour l'exécution, il existe une correspondance logique entre les variables et les instructions et les emplacements de mémoire du processeur et les séquences d'instructions du processeur. Une variable dont la valeur ne change jamais (par exemple une constante) ne nécessite même pas nécessairement un emplacement de mémoire, car la valeur peut être reproduite à volonté (par exemple, selon les besoins pour les séquences de code générées par le compilateur).
Est-il sûr de dire qu'une variable est la même chose qu'un emplacement mémoire?
Non. L'emplacement de la variable et de la mémoire sont deux abstractions à deux niveaux d'abstraction différents. La variable et les pointeurs sont un concept de niveau supérieur au niveau du code/langage, l'emplacement de mémoire est un concept de niveau inférieur au niveau de la machine. Une fois qu'un code a été compilé en un exécutable, il n'y a plus de variables. Essayer de parler de l'emplacement de la mémoire et des variables de cette manière est une erreur catégorique.
Une variable peut être implémentée à l'aide de la mémoire, mais pas toujours car un compilateur peut optimiser un calcul et effectuer tous les calculs relatifs à une variable entièrement dans des registres, ou il peut placer une seule variable dans plusieurs emplacements de mémoire, ou il peut utiliser une seule mémoire emplacement pour plusieurs variables.
Un pointeur est un emplacement mémoire dont la valeur est l'adresse mémoire d'un autre emplacement mémoire.
Cette série de cartes mémoire est tellement confuse, elles ne sont pas simplement incorrectes, mais elles n'ont même pas tort.
Les variables sont des constructions de langage. Ils ont un nom, résident dans une portée, peuvent être référencés par d'autres parties du code, etc. Ils sont une entité logique . Le compilateur est libre d'implémenter cette construction de langage comme bon lui semble, tant que le comportement observable est celui prescrit par la norme de langage. En tant que tel, la variable n'a même pas besoin d'être stockée n'importe où si le compilateur peut prouver que ce n'est pas nécessaire.
Les emplacements de mémoire sont un concept matériel. Ils signifient une place dans la mémoire virtuelle/physique. Chaque emplacement de mémoire a exactement une adresse physique et n'importe quelle quantité d'adresses virtuelles qui peuvent être utilisées pour la manipuler. Mais il y a toujours exactement un octet stocké à chaque emplacement de mémoire.
Les pointeurs sont un type spécial de valeurs . Dire que quelque chose est un pointeur revient à dire que quelque chose est de type double
. Cela signifie combien de bits sont utilisés pour la valeur et comment ces bits sont interprétés, mais cela ne signifie pas que cette valeur est stockée dans une variable, ni que cette valeur est stockée en mémoire.
Pour donner un exemple en C: Quand j'ai un tableau 2D int foo[6][7];
et j'en accède à un élément avec foo[1][2]
, puis foo
est une variable qui contient un tableau. Lorsque foo
est utilisé dans ce contexte, il est transformé en pointeur vers le premier élément du tableau. Ce pointeur n'est stocké dans aucune variable, ni stocké en mémoire, sa valeur est uniquement générée dans un registre du CPU, utilisée, puis oubliée. De même, l'expression foo[1]
est transformé en un autre pointeur dans ce contexte, qui, encore une fois, n'est pas dans une variable, n'est pas stocké en mémoire, mais calculé dans le CPU, utilisé et oublié. Les trois concepts variable , emplacement mémoire et pointeur sont en réalité trois concepts différents.
Btw, je voulais vraiment dire "il y a toujours exactement un octet stocké à chaque emplacement de mémoire". Ce n'était pas le cas à l'âge de pierre de l'informatique il y a une cinquantaine d'années, mais c'est vrai pour pratiquement tous les matériels utilisés aujourd'hui. Chaque fois que vous stockez une valeur en mémoire supérieure à un octet, vous utilisez en fait un certain nombre d'emplacements de mémoire consécutifs. C'est à dire. (en supposant un ordre d'octets de grande taille), le numéro 0x01234567 est stocké en mémoire sous la forme
+------+------+------+------+
| 0x01 | 0x23 | 0x45 | 0x67 |
+------+------+------+------+
^ ^ ^ ^
| | | |
0x4242 0x4243 0x4244 0x4245
(Les petites machines endiennes comme l'architecture X86 stockent les octets dans l'ordre inverse.) Cela est également vrai pour les pointeurs: un pointeur sur une machine 64 bits est stocké dans huit octets consécutifs, chacun avec sa propre adresse mémoire. Vous ne pouvez pas regarder une cellule mémoire et dire: "Oh, c'est un pointeur!" Vous ne voyez toujours que des octets lorsque vous regardez la mémoire.
Permettez-moi de me concentrer sur votre question réelle - "qui a raison?" lorsque vous comparez ces deux déclarations:
La réponse à cette question est aucun. La première parle d'une "adresse mémoire d'une autre variable", mais les variables n'ont pas nécessairement d'adresse mémoire, comme les autres réponses l'ont déjà expliqué. Le second dit "un pointeur est un emplacement mémoire", mais un pointeur est littéralement juste un nombre, qui peut être stocké dans une variable, mais comme auparavant, une variable n'a pas nécessairement une adresse mémoire.
Quelques exemples de déclarations plus précises:
"Un pointeur est un nombre représentant l'adresse mémoire d'un emplacement mémoire", ou
"Une variable de pointeur est une variable dont la valeur est l'adresse mémoire d'un emplacement mémoire."
"Une adresse mémoire peut contenir un pointeur représentant l'adresse mémoire d'un emplacement mémoire."
Notez parfois que le terme "pointeur" est utilisé comme raccourci pour "variable pointeur", ce qui est correct tant qu'il ne crée pas de confusion.
Je ne dirais certainement pas qu'un pointeur est un emplacement mémoire qui contient une adresse. D'une part, je ne connais pas d'architecture où 0x23453
pourrait tenir dans un seul octet. :) Même si vous supprimez la distinction octet/mot, vous avez toujours le problème que chaque emplacement de mémoire contient une adresse. Les adresses ne sont que des nombres et le contenu de la mémoire n'est que des nombres.
Je pense que l'astuce ici est que "pointeur" décrit l'intention humaine , pas une caractéristique particulière de l'architecture. C'est similaire à la façon dont un "caractère" ou une "chaîne" n'est pas une chose concrète que vous pouvez voir en mémoire - ce ne sont que des nombres, mais ils fonctionnent comme des chaînes parce que c'est ainsi qu'ils sont traités. "Pointeur" signifie simplement une valeur destinée à être utilisée comme adresse.
Honnêtement, si votre objectif est d'enseigner une langue particulière (Objectif C?), Je ne suis pas sûr que tirer la bande mémoire classique soit même utile . Vous dites déjà des mensonges blancs en affichant des valeurs tapées et des valeurs trop grandes pour un octet. Enseignez la sémantique, pas la mécanique - la principale idée des pointeurs est qu'ils fournissent indirection, qui est un outil extrêmement utile à comprendre.
Je pense qu'une bonne comparaison pourrait être avec une URL, qui vous indique où pour trouver des données, mais ce ne sont pas les données elles-mêmes. Écoutez-moi:
Vous vous souciez rarement de l'URL ; la grande majorité d'entre eux sont écervelés dans les liens avec les noms. Beaucoup de gens utilisent Internet sans savoir exactement comment une URL se traduit par une page; certaines personnes sont totalement inconscientes des URL.
Toutes les chaînes ne sont pas des URL ou ne sont pas destinées à être utilisées comme URL.
Si vous essayez de visiter une URL bidon ou une page qui existait auparavant mais qui a depuis été supprimée, vous obtenez une erreur.
Une URL peut pointer vers une image, du texte, de la musique ou un certain nombre d'autres éléments individuels - ou elle peut pointer vers une page contenant une variété d'éléments. Il est très courant d'avoir tout un tas de pages avec des mises en page similaires mais des données différentes.
Si vous créez une page Web et que vous souhaitez faire référence à des données sur une autre page Web, vous n'avez pas besoin de tout copier et coller; vous pouvez simplement y faire un lien.
Un nombre illimité d'autres pages peuvent renvoyer vers la même URL.
Si vous avez une collection de pages similaires, vous pouvez créer une page d'index qui répertorie tous les liens, ou vous pouvez simplement avoir un lien "suivant" au bas de la page 1 qui vous amène à la page 2, et ainsi de suite. Les avantages et les inconvénients des deux approches sont immédiatement évidents, surtout si vous considérez ce que le webmaster devrait faire pour ajouter ou supprimer des pages à divers endroits.
Cette analogie rend très clair quels sont les pointeurs pour , ce qui est essentiel pour les comprendre - sinon ils semblent juste arbitraires, compliqués et inutile. Comprendre comment quelque chose fonctionne est beaucoup plus facile si vous comprenez déjà ce qu'il fait et pourquoi il est utile. Si vous avez déjà internalisé qu'un pointeur est une boîte noire qui vous indique où se trouve autre chose, et alors vous apprenez les subtilités du modèle de mémoire , alors représenter des pointeurs comme des adresses est assez évident. De plus, l'enseignement de la sémantique mettra vos élèves dans un bien meilleur endroit pour comprendre et inventer d'autres formes d'indirection - ce qui est bien lorsque la plupart des langues principales n'ont pas de pointeurs!
Je sais que vous avez déjà accepté une réponse, et cette question a déjà cinq réponses, mais il y a un point qu'ils ne mentionnent pas, un qui, je pense, vous a fait trébucher. Les manuels CS essaient souvent d'être agnostiques quant au choix du langage de programmation, ce qui conduit à l'hypothèse implicite que la terminologie utilisée pour décrire les choses est universelle. Ça ne l'est pas.
En C, l'opérateur esperluette unaire est appelé opérateur "adresse de". Les programmeurs C n'hésiteraient pas à dire que l'expression &x
évalue l'adresse de la variable x. Bien sûr, ils signifient "l'adresse mémoire dans laquelle la valeur de la variable x est stockée", mais personne n'est aussi pédant dans une conversation informelle. En C, le mot "pointeur" se réfère généralement au type de données d'une variable destinée à avoir une adresse mémoire comme valeur. Ou de manière équivalente le type de données de la valeur. Mais certaines personnes utiliseraient "pointeur" comme valeur elle-même.
En Java, toutes les variables de type objet ou tableau se comportent un peu comme des pointeurs C (à l'exception de l'arithmétique des pointeurs), mais Java les appellent des références, pas des pointeurs).
C++ considère les références et les pointeurs comme des concepts différents. Ils sont liés, mais pas tout à fait la même chose, donc les programmeurs C++ doivent faire la distinction dans la conversation. L'esperluette est lue comme "adresse de" dans certains contextes et "référence à" dans d'autres.
Un pointeur est une variable dont la valeur est l'adresse mémoire d'une autre variable.
Voilà comment un programmeur C pourrait le décrire, en utilisant "un pointeur" dans le même sens que "un int." (Comme dans, "un pointeur contient une adresse mémoire tandis qu'un int contient un entier dans une certaine plage.")
Un pointeur est un emplacement mémoire dont la valeur est l'adresse mémoire d'un autre emplacement mémoire.
C'est une façon étrange de le dire, car cela nécessite une définition très vague et informelle du "est".
Est-il sûr de dire qu'une variable est la même chose qu'un emplacement mémoire?
Il serait plus clair de dire qu'une adresse mémoire est l'emplacement en mémoire où la valeur d'une variable est stockée. (Certes, toutes les variables ne sont pas stockées en mémoire, en raison des optimisations du compilateur, mais toute variable dont l'adresse est prise avec &x
sera.)
L'instruction n pointeur est une variable dont la valeur est l'adresse mémoire d'une autre variable est trop simplifiée. Mais au moment où un lecteur comprend exactement ce qu'est un emplacement de mémoire et comment il diffère d'une variable, il comprendra déjà ce qu'est exactement un pointeur et n'aura donc plus besoin de s'appuyer sur cette explication inexacte.
L'instruction n pointeur est un emplacement mémoire dont la valeur est l'adresse mémoire d'un autre emplacement mémoire est incorrecte. La valeur d'un pointeur n'a pas besoin d'être stockée dans un emplacement mémoire, et elle est discutable si un pointeur doit pointer vers un emplacement mémoire, selon la définition voulue de "mémoire".
Quelle est la différence entre une variable et un emplacement mémoire
Un emplacement de mémoire est l'un des multiples emplacements possibles où les données peuvent être stockées. Ces données peuvent être une variable ou une partie d'une variable. Les variables sont un moyen d'étiqueter les données.
Cette réponse se concentre sur C et C++; cela semble approprié car votre question concerne les pointeurs qui font partie intégrante de C/C++ que d'autres langages. La plupart de ce post s'appliquera à la plupart des langages compilés sans temps d'exécution élaboré (comme Pascal ou Ada, mais pas comme Java ou C #).
Les bonnes réponses déjà données soulignent qu'une variable est un langage construit à un niveau plus abstrait que la mémoire physique. Je voudrais souligner cependant que cette abstraction a une certaine logique et un certain système:
L'abstraction consiste principalement à utiliser un nom au lieu d'une adresse littérale.
L'idée principale est qu'une variable est une poignée nommée pour un objet typé; les objets en C/C++ sont généralement en mémoire. Les langages ajoutent ensuite quelques subtilités concernant la gestion de la durée de vie et le marshaling des données pour les conversions de types. Le concept de variables est plus abstrait que les adresses physiques car nous ne nous soucions pas réellement de la valeur numérique des adresses ou de l'emplacement exact des fonctions en mémoire. Nous les nommons simplement et les adressons plus tard par leur nom, et le compilateur, l'éditeur de liens et le système d'exécution s'occupent des détails les plus précis.
Et ne prétendez pas que C/C++ est indépendant de la mémoire: il y a, après tout, l'opérateur d'adresse universellement applicable. Oui, c'est vrai, vous ne pouvez pas prendre l'adresse d'une variable C dans la classe de stockage de registre; mais quand en avez-vous utilisé un pour la dernière fois? Il s'agit d'une exception spéciale au concept général, et non d'un rejet total de l'argument. La règle générale est, au contraire, que la prise d'une adresse d'une variable oblige en fait le compilateur à créer effectivement un objet en mémoire, même s'il ne le ferait pas autrement (par exemple avec des constantes). Le concept de "poignée nommée" est également un bon paradigme pour les références C++: Une référence est juste un autre nom pour le même objet.
Quand j'ai écrit l'assembleur en ligne pour 68k, c'était agréable de voir comment vous pouviez utiliser des noms de variables comme décalages pour adresser des registres (et vous pouviez utiliser les noms de variables déclarées register
au lieu des noms de registres nus!). Pour le compilateur, une variable est un décalage d'adresse constant. Pour réitérer: les variables sont des poignées nommées, généralement pour les objets en mémoire.
Il semble que la question vise un langage populaire formé en augmentant la norme C avec la garantie supplémentaire "Dans les cas où certaines parties de la norme ou la documentation d'une implémentation décrivent le comportement d'une action, et une autre partie la classe comme indéfinie , la première partie domine. ", ainsi qu'une définition de" variable "compatible avec l'utilisation du terme par d'autres langues.
Dans cette langue, chaque emplacement de mémoire peut être vu comme une boîte aux lettres numérotée qui contient toujours un certain nombre (généralement huit) de bits, chacun pouvant être indépendamment zéro ou un. Les emplacements de mémoire sont généralement organisés en rangées de deux, quatre ou huit. et certaines opérations traitent simultanément plusieurs emplacements de mémoire consécutifs. Selon la machine, certaines opérations qui opèrent sur des groupes de deux, quatre ou huit emplacements de mémoire peuvent se limiter à fonctionner sur des emplacements sur une seule ligne. En outre, alors que certaines machines peuvent avoir une seule salle de boîtes aux lettres numérotées consécutivement, d'autres peuvent avoir plusieurs groupes disjoints de boîtes aux lettres numérotées.
Une variable identifie une plage d'emplacements de mémoire qui lui sont associés exclusivement, et un type dans lequel ces emplacements de mémoire doivent être interprétés. La lecture d'une variable entraînera l'interprétation des bits dans ses emplacements de stockage associés d'une manière appropriée au type de variable, et l'écriture d'une variable entraînera la définition des bits associés d'une manière appropriée à son type et à sa valeur.
Une adresse encapsule toutes les informations nécessaires pour identifier une boîte aux lettres. Cela peut être stocké sous la forme d'un numéro simple ou sous la forme d'une sorte d'indicateur de groupe avec le numéro d'une boîte aux lettres au sein de ce groupe.
L'application de l'opérateur &
À une variable produira un pointeur qui encapsule l'adresse et son type. L'application de l'opérateur unaire *
Ou []
À un pointeur provoquera l'interprétation ou la définition des bits des boîtes aux lettres commençant à l'adresse encapsulée d'une manière appropriée au type encapsulé.
Je viens en retard à cette fête mais je ne peux pas résister à mettre mes 2 cents.
À ces moments, quelle est la différence entre les valeurs stockées dans ces emplacements de mémoire?
Temps 1
Temps 2
Bonne réponse: rien. Ce sont toutes des valeurs identiques présentées avec des interprétations différentes de leur signification.
Comment le sais-je? Parce que c'est moi qui ai inventé ça. Vous ne le savez pas encore vraiment.
Vous rencontrez quelque chose que j'appelle le problème hors bande . Comment interpréter correctement la signification de ces valeurs n'est pas stocké ici. Cette connaissance est stockée ailleurs. Pourtant, lorsque vous présentez ces valeurs sur papier, vous mettez cette interprétation. Cela signifie que vous avez ajouté des informations qui n'existent tout simplement pas dans ces emplacements de mémoire.
Par exemple, les valeurs ici sont identiques, mais vous ne savez que pour être vrai si vous avez raison lorsque vous supposez un ASCII /le codage de caractères UTF-8 est comment j'ai obtenu le premier, plutôt que de dire EBCDIC . Et vous devez également supposer que la seconde est des expressions hexadécimales des valeurs numériques stockées à ces emplacements de mémoire, qui pourraient toutes être des pointeurs vers d'autres adresses, plutôt que de dire des références à des chaînes qui commencent toutes par "0x". : P
Rien stocké dans ces emplacements de mémoire ne vous indique que ces hypothèses sont correctes. Ces informations peuvent être stockées. Mais il serait stocké ailleurs.
C'est le problème de présentation . Vous ne pouvez pas exprimer de chiffre du tout sans d'abord vous mettre d'accord sur la manière de le présenter. Vous pouvez vous appuyer sur des hypothèses, des conventions et du contexte, mais si vous y réfléchissez profondément, lorsque la présentation n'est pas explicitement définie, la seule réponse vraiment correcte est "pas assez d'informations".