224 votes

Qu'est-ce qu'un pointeur C, sinon une adresse mémoire ?

Dans une source réputée sur C, les informations suivantes sont données après avoir discuté de la & opérateur :

... C'est un peu malheureux que la terminologie [adresse de] reste, parce qu'il sème la confusion chez ceux qui ne savent pas ce que sont les adresses, et induit en erreur ceux qui le savent : penser aux pointeurs comme s'il s'agissait d'adresses conduit généralement à des ennuis...

D'autres documents que j'ai lus (de sources tout aussi réputées, je dirais) faisaient toujours référence sans ambages aux pointeurs et au & opérateur que de donner des adresses de mémoire. J'aimerais continuer à chercher la vérité sur cette question, mais c'est difficile lorsque des sources fiables ne sont pas d'accord.

Maintenant, je suis un peu confus - ce que exactement est un pointeur, alors, si ce n'est pas une adresse mémoire ?

P.S.

L'auteur dit plus tard : ...je continuerai cependant à utiliser le terme "adresse de", parce que pour en inventer une autre [terme] serait encore pire.

127 votes

Un pointeur est un variable que tient une adresse. Il possède également son propre adresse. C'est la différence fondamentale entre un pointeur et un tableau. Un tableau est effectivement es une adresse (et par implication, son adresse est lui-même ).

3 votes

@WhozCraig En effet, mais la citation tient particulièrement à noter que & ne renvoie pas une adresse mémoire réelle. Que renvoie-t-il alors ?

0 votes

160voto

Alexey Frunze Points 37651

La norme C ne définit pas ce qu'est un pointeur en interne et comment il fonctionne en interne. Ceci est intentionnel afin de ne pas limiter le nombre de plates-formes, où le C peut être implémenté comme un langage compilé ou interprété.

La valeur d'un pointeur peut être une sorte d'ID ou de handle ou une combinaison de plusieurs ID (dites bonjour aux segments et offsets x86) et pas nécessairement une adresse mémoire réelle. Cet ID peut être n'importe quoi, même une chaîne de texte de taille fixe. Les représentations sans adresse peuvent être particulièrement utiles pour un interprète C.

0 votes

Pourriez-vous nous expliquer un peu ce que sont les handlers/ID ? Je pense que cela m'aidera à mieux comprendre. Je n'ai jamais rencontré ces termes jusqu'à présent !

36 votes

Il n'y a pas grand-chose à expliquer. Chaque variable a son adresse en mémoire. Mais vous n'êtes pas obligé de stocker leurs adresses dans les pointeurs vers elles. Au lieu de cela, vous pouvez numéroter vos variables de 1 à n'importe quoi et stocker ce numéro dans le pointeur. C'est parfaitement légal selon la norme du langage tant que l'implémentation sait comment transformer ces nombres en adresses et comment faire de l'arithmétique de pointeur avec ces nombres et toutes les autres choses requises par la norme.

0 votes

C'est bien, mais pourquoi pas ceci : int i = 555, *p = &i ; p++ ; // c'est un C et un C++ valides et cela ne fonctionnera que si les pointeurs sont des adresses.

64voto

Cornstalks Points 9261

Je ne suis pas sûr de votre source, mais le type de langage que vous décrivez provient de la norme C :

6.5.3.2 Opérateurs d'adresse et d'indirection
[...]
3. L'opérateur unaire & donne l'adresse de son opérande. [...]

Donc... oui, les pointeurs pointent vers des adresses mémoire. Du moins, c'est ce que la norme C suggère de signifier.

Pour le dire un peu plus clairement, un pointeur est une variable contenant le valeur de certains adresse . L'adresse d'un objet (qui peut être stocké dans un pointeur) est retournée avec la fonction unaire & opérateur.

Je peux stocker l'adresse "42 Wallaby Way, Sydney" dans une variable (et cette variable serait une sorte de "pointeur", mais comme il ne s'agit pas d'une adresse mémoire, on ne peut pas parler de "pointeur"). Votre ordinateur a des adresses pour ses blocs de mémoire. Les pointeurs stockent la valeur d'une adresse (par exemple, un pointeur stocke la valeur "42 Wallaby Way, Sydney", qui est une adresse).

Edita: Je voudrais développer le commentaire d'Alexey Frunze.

Qu'est-ce qu'un pointeur ? Examinons la norme C :

6.2.5 Types
[...]
20. [...]
A Type de pointeur peut être dérivé d'un type de fonction ou d'un type d'objet, appelé le Type de référence . Un type pointeur décrit un objet dont la valeur fournit une référence à une entité du type référencé. Un type pointeur dérivé du type référencé T est parfois appelé ''pointeur vers T''. La construction d'un type pointeur à partir d'un type référencé est appelée ''dérivation de type pointeur''. Un type pointeur est un type d'objet complet.

Essentiellement, les pointeurs stockent une valeur qui fournit une référence à un objet ou une fonction. En quelque sorte. Les pointeurs sont destinés à stocker une valeur qui fournit une référence à un objet ou une fonction, mais ce n'est pas le cas. toujours l'affaire :

6.3.2.3 Pointeurs
[...]
5. Un nombre entier peut être converti en n'importe quel type de pointeur. Sauf indication contraire, le résultat est défini par l'implémentation, peut ne pas être correctement aligné, peut ne pas pointer vers une entité du type référencé, et peut être une représentation piège.

La citation ci-dessus dit que nous pouvons transformer un entier en un pointeur. Si nous faisons cela (c'est-à-dire si nous plaçons une valeur entière dans un pointeur au lieu d'une référence spécifique à un objet ou une fonction), alors le pointeur "pourrait ne pas pointer vers une entité de type référence" (c'est-à-dire qu'il pourrait ne pas fournir une référence à un objet ou une fonction). Il pourrait nous fournir quelque chose d'autre. Et c'est l'un des endroits où vous pouvez coller une sorte de poignée ou d'ID dans un pointeur (c'est-à-dire que le pointeur ne pointe pas vers un objet ; il stocke une valeur qui représente quelque chose, mais cette valeur peut ne pas être une adresse).

Donc oui, comme le dit Alexey Frunze, il est possible qu'un pointeur ne stocke pas l'adresse d'un objet ou d'une fonction. Il est possible qu'un pointeur stocke à la place une sorte de "handle" ou d'ID, et vous pouvez le faire en assignant une valeur entière arbitraire à un pointeur. Ce que ce handle ou ID représente dépend du système/environnement/contexte. Tant que votre système/implémentation peut donner un sens à la valeur, tout va bien (mais cela dépend de la valeur spécifique et du système/implémentation spécifique).

Normalement un pointeur stocke l'adresse d'un objet ou d'une fonction. S'il ne stocke pas une adresse réelle (vers un objet ou une fonction), le résultat est défini par l'implémentation (ce qui signifie que ce qui se passe exactement et ce que le pointeur représente maintenant dépend de votre système et de l'implémentation, il peut donc s'agir d'un handle ou d'un ID sur un système particulier, mais l'utilisation du même code/valeur sur un autre système peut faire planter votre programme).

Ça a fini par être plus long que je ne le pensais...

4 votes

Dans un interpréteur C, un pointeur peut contenir un ID/handle/etc non adressé.

0 votes

L'interpréteur C n'est pas le langage C décrit par la norme. C'est le "C interprété" qui est un animal différent, et non standardisé pour autant que je sache.

1 votes

@AlexeyFrunze : J'ai ajouté une (longue) extension à votre commentaire. N'hésitez pas à critiquer.

38voto

Gilles Points 37537

Penser à un pointeur comme à une adresse est un approximation . Comme toutes les approximations, elle est suffisamment bonne pour être utile parfois, mais elle n'est pas non plus exacte, ce qui signifie que s'y fier peut poser problème.

Un pointeur est comme une adresse dans la mesure où il indique où trouver un objet. Une limitation immédiate de cette analogie est que tous les pointeurs ne contiennent pas réellement une adresse. NULL est un pointeur qui n'est pas une adresse. Le contenu d'une variable pointeur peut en fait être de l'une des trois sortes suivantes :

  • le site adresse d'un objet, qui peut être déréférencé (si p contient l'adresse de x alors l'expression *p a la même valeur que x ) ;
  • a pointeur nul dont NULL est un exemple ;
  • invalide qui ne pointe pas vers un objet (si p ne contient pas de valeur valide, alors *p pourrait faire n'importe quoi ("comportement indéfini"), le plantage du programme étant une possibilité assez courante).

En outre, il serait plus exact de dire qu'un pointeur (s'il est valide et non nul) contient une adresse : un pointeur indique où trouver un objet, mais il est lié à d'autres informations.

En particulier, un pointeur a un type. Sur la plupart des plateformes, le type du pointeur n'a aucune influence au moment de l'exécution, mais il a une influence qui va au-delà du type au moment de la compilation. Si p est un pointeur vers int ( int *p; ), alors p + 1 pointe vers un nombre entier qui est sizeof(int) octets après p (en supposant que p + 1 est toujours un pointeur valide). Si q est un pointeur vers char qui pointe vers la même adresse que p ( char *q = p; ), alors q + 1 n'est pas la même adresse que p + 1 . Si vous considérez les pointeurs comme des adresses, il n'est pas très intuitif que la "prochaine adresse" soit différente pour différents pointeurs vers le même emplacement.

Dans certains environnements, il est possible d'avoir plusieurs valeurs de pointeur avec des représentations différentes (différents modèles de bits en mémoire) qui pointent vers le même emplacement en mémoire. Vous pouvez penser à ces valeurs comme à différents pointeurs détenant la même adresse, ou comme à différentes adresses pour le même emplacement - la métaphore n'est pas claire dans ce cas. Le site == vous indique toujours si les deux opérandes pointent vers le même endroit, donc sur ces environnements vous pouvez avoir p == q même si p y q ont des schémas binaires différents.

Il existe même des environnements où les pointeurs portent d'autres informations que l'adresse, comme le type ou les informations de permission. Vous pouvez facilement passer votre vie de programmeur sans les rencontrer.

Il existe des environnements où différents types de pointeurs ont des représentations différentes. C'est un peu comme si différents types d'adresses avaient des représentations différentes. Par exemple, certaines architectures ont des pointeurs d'octets et des pointeurs de mots, ou des pointeurs d'objets et des pointeurs de fonctions.

En somme, considérer les pointeurs comme des adresses n'est pas si mal, tant que vous gardez à l'esprit que

  • ce ne sont que les pointeurs valides et non nuls qui sont des adresses ;
  • vous pouvez avoir plusieurs adresses pour un même lieu ;
  • on ne peut pas faire d'arithmétique sur les adresses, et il n'y a pas d'ordre sur elles ;
  • le pointeur porte également des informations sur le type.

L'inverse est bien plus gênant. Tout ce qui ressemble à une adresse ne peut pas être un pointeur. . Quelque part, au fond, tout pointeur est représenté par un motif de bits qui peut être lu comme un nombre entier, et on peut dire que ce nombre entier est une adresse. Mais dans l'autre sens, tous les entiers ne sont pas des pointeurs.

Il y a d'abord quelques limitations bien connues ; par exemple, un nombre entier qui désigne un emplacement en dehors de l'espace d'adressage de votre programme ne peut pas être un pointeur valide. Une adresse mal alignée ne constitue pas un pointeur valide pour un type de données qui nécessite un alignement ; par exemple, sur une plateforme où int nécessite un alignement sur 4 octets, 0x7654321 ne peut pas être une valeur valide. int* valeur.

Cependant, cela va bien au-delà, car lorsque vous transformez un pointeur en un nombre entier, vous vous exposez à de nombreux problèmes. Une grande partie de ces problèmes est due au fait que les compilateurs optimisateurs sont bien meilleurs en micro-optimisation que ce à quoi la plupart des programmeurs s'attendent, de sorte que leur modèle mental du fonctionnement d'un programme est profondément erroné. Ce n'est pas parce que vous avez des pointeurs avec la même adresse qu'ils sont équivalents. Par exemple, considérez le bout de phrase suivant :

unsigned int x = 0;
unsigned short *p = (unsigned short*)&x;
p[0] = 1;
printf("%u = %u\n", x, *p);

On pourrait s'attendre à ce que sur une machine ordinaire où sizeof(int)==4 y sizeof(short)==2 ce qui permet soit d'imprimer 1 = 1? (little-endian) ou 65536 = 1? (big-endian). Mais sur mon PC Linux 64 bits avec GCC 4.4 :

$ c99 -O2 -Wall a.c && ./a.out 
a.c: In function ‘main’:
a.c:6: warning: dereferencing pointer ‘p’ does break strict-aliasing rules
a.c:5: note: initialized from here
0 = 1?

GCC a la gentillesse de nous avertir de ce qui ne va pas dans cet exemple simple - dans des exemples plus complexes, le compilateur pourrait ne pas le remarquer. Puisque p a un type différent de &x en changeant ce qui p ne peut pas affecter ce que &x pointe (en dehors de certaines exceptions bien définies). Par conséquent, le compilateur est libre de garder la valeur de x dans un registre et ne pas mettre à jour ce registre comme *p changements. Le programme déréférence deux pointeurs à la même adresse et obtient deux valeurs différentes !

La morale de cet exemple est que l'on peut considérer un pointeur (valide et non nul) comme une adresse, tant que l'on respecte les règles précises du langage C. Le revers de la médaille est que les règles du langage C sont complexes et difficiles à appréhender intuitivement, à moins de savoir ce qui se passe sous le capot. Et ce qui se passe sous le capot est que le lien entre les pointeurs et les adresses est quelque peu relâché, à la fois pour supporter les architectures de processeurs "exotiques" et pour supporter les compilateurs optimisateurs.

Pensez donc que les pointeurs sont des adresses comme une première étape dans votre compréhension, mais ne suivez pas cette intuition trop loin.

5 votes

+1. Les autres réponses semblent oublier qu'un pointeur est accompagné d'informations sur le type. C'est bien plus important que la discussion sur l'adresse, l'ID ou autre.

0 votes

+1 Excellents points sur les informations de type. Je ne suis pas sûr que les exemples du compilateur soient corrects, mais... Il semble très peu probable, par exemple, que *p = 3 est garanti de réussir lorsque p n'a pas été initialisé.

0 votes

@LarsH Vous avez raison, merci, comment ai-je pu écrire cela ? Je l'ai remplacé par un exemple qui démontre même le comportement surprenant sur mon PC.

37voto

Harikrishnan Points 1049

Pointer vs Variable

Sur cette photo,

pointer_p est un pointeur situé à 0x12345, et pointe vers une variable variable_v à 0x34567.

20 votes

Non seulement cela ne tient pas compte de la notion d'adresse par opposition à celle de pointeur, mais cela ne tient pas compte du fait qu'une adresse n'est pas seulement un nombre entier.

21 votes

-1, cela explique juste ce qu'est un pointeur. Ce n'était pas la question-- et vous mettez de côté toutes les complexités que la question es sur.

19voto

Aniket Points 15250

Un pointeur est une variable qui contient une adresse mémoire, et non l'adresse elle-même. Cependant, vous pouvez déréférencer un pointeur - et accéder à l'emplacement de la mémoire.

Par exemple :

int q = 10; /*say q is at address 0x10203040*/
int *p = &q; /*means let p contain the address of q, which is 0x10203040*/
*p = 20; /*set whatever is at the address pointed by "p" as 20*/

C'est tout. C'est aussi simple que ça.

enter image description here

Un programme pour démontrer ce que je dis et son résultat est ici :

http://ideone.com/rcSUsb

Le programme :

#include <stdio.h>

int main(int argc, char *argv[])
{
  /* POINTER AS AN ADDRESS */
  int q = 10;
  int *p = &q;

  printf("address of q is %p\n", (void *)&q);
  printf("p contains %p\n", (void *)p);

  p = NULL;
  printf("NULL p now contains %p\n", (void *)p);
  return 0;
}

5 votes

Cela peut créer encore plus de confusion. Alice, peux-tu voir un chat ? Non, je vois seulement le sourire d'un chat. Donc dire que le pointeur est une adresse ou que le pointeur est une variable qui contient une adresse ou dire que le pointeur est le nom d'un concept qui se réfère à l'idée d'une adresse, jusqu'où les auteurs de livres peuvent-ils aller pour confondre les néophytes ?

0 votes

@exebook à ceux qui sont aguerris aux pointeurs, c'est assez simple. Peut-être qu'une photo vous aidera ?

5 votes

Un pointeur ne contient pas nécessairement une adresse. Dans un interpréteur C, il peut s'agir d'autre chose, d'une sorte d'ID/manipulation.

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