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

Dans une source digne de confiance sur C, les informations suivantes sont données après avoir discuté de l’opérateur & :

… C’est un peu dommage que la terminologie [adresse de] rest, car elle confond ceux qui ne connaissent pas les adresses et induit en erreur ceux qui le font: penser aux pointeurs comme s’ils étaient des adresses mène généralement à la peine. .

D’autres documents que j’ai lus (provenant de sources tout aussi réputées, je dirais,) ont toujours fait référence sans vergogne aux pointeurs et à l’opérateur & donnant des adresses de mémoire. J’aimerais continuer à chercher l’actualité, mais c’est un peu difficile quand des sources fiables ne sont pas d’accord.

Maintenant, je suis un peu confus – qu’est-ce qu’un pointeur, alors, si ce n’est une adresse mémoire?

PS

L’auteur dit plus tard: « Je continuerai à utiliser le terme« adresse de », car inventer un autre terme serait encore pire.

Le standard C ne définit pas ce qu’est un pointeur en interne et son fonctionnement interne. Ceci est intentionnel afin de ne pas limiter le nombre de plates-formes, où C peut être implémenté en tant que langage compilé ou interprété.

Une valeur de pointeur peut être une sorte d’ID ou de descripteur ou une combinaison de plusieurs identifiants (dire bonjour aux segments et décalages x86) et pas nécessairement une véritable adresse mémoire. Cet identifiant 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éteur C.

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

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

Donc … oui, les pointeurs pointent vers les adresses mémoire. C’est du moins ce que la norme C suggère.

Pour le dire un peu plus clairement, un pointeur est une variable contenant la valeur d’une adresse . L’adresse d’un object (qui peut être stocké dans un pointeur) est renvoyée avec l’opérateur unaire & .

Je peux stocker l’adresse “42 Wallaby Way, Sydney” dans une variable (et cette variable serait un “pointeur” de toutes sortes, mais comme ce n’est pas une adresse mémoire, ce n’est pas quelque chose que nous appellerions un “pointeur”). Votre ordinateur a des adresses pour ses compartiments de mémoire. Les pointeurs stockent la valeur d’une adresse (un pointeur stocke la valeur “42 Wallaby Way, Sydney”, qui est une adresse).

Edit: Je veux développer le commentaire d’Alexey Frunze.

Qu’est-ce qu’un pointeur? Regardons le standard C:

6.2.5 Types
[…]
20. […]
Un type de pointeur peut être dérivé d’un type de fonction ou d’un type d’object, appelé type référencé . Un type de pointeur décrit un object dont la valeur fournit une référence à une entité du type référencé. Un type de pointeur dérivé du type référencé T est parfois appelé “pointeur sur T”. La construction d’un type de pointeur à partir d’un type référencé est appelée “dérivation de type pointeur”. Un type de pointeur est un type d’object complet.

Essentiellement, les pointeurs stockent une valeur qui fournit une référence à un object ou à une fonction. Genre de. Les pointeurs sont destinés à stocker une valeur qui fournit une référence à un object ou une fonction, mais ce n’est pas toujours le cas:

6.3.2.3 Pointeurs
[…]
5. Un entier peut être converti en n’importe quel type de pointeur. À l’exception de ce qui a été spécifié précédemment, le résultat est défini par l’implémentation, peut ne pas être correctement aligné, peut ne pas pointer sur une entité du type référencé et peut être une représentation d’interruption.

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 object ou à une fonction), alors le pointeur “pourrait ne pas pointer vers une entité de type référence”. référence à un object ou une fonction). Cela pourrait nous donner autre chose. Et c’est un endroit où vous pouvez coller une sorte de poignée ou d’ID dans un pointeur (le pointeur ne pointe pas vers un object, 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 d’adresse à un object ou à 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 représente cette poignée ou cet ID dépend du système / environnement / contexte. Tant que votre système / implémentation peut donner un sens à la valeur, vous êtes en bonne forme (mais cela dépend de la valeur spécifique et du système / implémentation spécifique).

Normalement , un pointeur stocke une adresse dans un object ou une fonction. S’il ne stocke pas d’adresse réelle (à un object ou à une fonction), le résultat est défini par l’implémentation (ce qui signifie que ce qui se passe et ce que le pointeur représente maintenant dépend de votre système et de votre implémentation). un système particulier, mais en utilisant le même code / la même valeur sur un autre système pourrait faire planter votre programme).

Cela a fini par être plus long que ce que je pensais que ce serait …

Pointeur vs Variable

Sur cette photo,

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

Considérer un pointeur comme une adresse est une approximation . Comme toutes les approximations, c’est assez bon pour être utile parfois, mais ce n’est pas non plus exact, ce qui signifie que le fait de s’en servir pose des problèmes.

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

  • l’ adresse d’un object, qui peut être déréférencée (si p contient l’adresse de x l’expression *p a la même valeur que x );
  • un pointeur nul dont NULL est un exemple;
  • contenu invalide , qui ne pointe pas vers un object (si p ne contient pas de valeur valide, alors *p peut tout faire («comportement indéfini»), avec une possibilité de plantage du programme).

De plus, 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 object, mais il y a plus d’informations liées à celui-ci.

En particulier, un pointeur a un type. Sur la plupart des plates-formes, le type du pointeur n’a aucune influence au moment de l’exécution, mais son influence dépasse le type au moment de la compilation. Si p est un pointeur sur int ( int *p; ), alors p + 1 pointe sur un entier qui est sizeof(int) bytes après p (en supposant que p + 1 est toujours un pointeur valide). Si q est un pointeur sur char qui pointe sur 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 le pointeur comme une adresse, 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 (modèles de bits différents en mémoire) qui pointent vers le même emplacement en mémoire. Vous pouvez les considérer comme des pointeurs différents portant la même adresse ou comme des adresses différentes pour le même emplacement – la métaphore n’est pas claire dans ce cas. L’opérateur == vous indique toujours si les deux opérandes pointent vers le même emplacement. Dans ces environnements, vous pouvez avoir p == q même si p et q ont des modèles de bits différents.

Il existe même des environnements dans lesquels des pointeurs transportent d’autres informations au-delà de l’adresse, telles que des informations de type ou d’autorisation. Vous pouvez facilement traverser votre vie en tant que programmeur sans les rencontrer.

Il existe des environnements où différents types de pointeurs ont des représentations différentes. Vous pouvez le voir comme différents types d’adresses ayant des représentations différentes. Par exemple, certaines architectures ont des pointeurs d’octets et des pointeurs de mots, ou des pointeurs d’objects et des fonctions.

Dans l’ensemble, penser aux pointeurs comme des adresses n’est pas trop mal tant que vous gardez à l’esprit que

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

Aller dans l’autre sens est beaucoup plus compliqué. Tout ce qui ressemble à une adresse ne peut pas être un pointeur . Quelque part au fond, tout pointeur est représenté comme un modèle de bit pouvant être lu comme un entier et vous pouvez dire que cet entier est une adresse. Mais inversement, tous les entiers ne sont pas des pointeurs.

Il y a d’abord quelques limitations bien connues; Par exemple, un 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 crée pas un pointeur valide pour un type de données nécessitant un alignement; Par exemple, sur une plate-forme où int nécessite un alignement sur 4 octets, 0x7654321 ne peut pas être une valeur int* valide.

Cependant, cela va bien au-delà, car lorsque vous créez un pointeur dans un nombre entier, vous rencontrerez des problèmes. Une grande partie de ces problèmes réside dans le fait que l’optimisation des compilateurs est bien meilleure en matière de microoptimisation que ce que la plupart des programmeurs attendent, de sorte que leur modèle mental de fonctionnement d’un programme est profondément erroné. Juste parce que vous avez des pointeurs avec la même adresse ne signifie pas qu’ils sont équivalents. Par exemple, considérez l’extrait de code suivant:

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

Vous pouvez vous attendre à ce que sur une machine sizeof(int)==4sizeof(int)==4 et sizeof(short)==2 , ceci soit 1 = 1? (little-endian) ou 65536 = 1? (big-endian). Mais sur mon PC Linux 64 bits avec GCC 4.4:

 $ c99 -O2 -Wall ac && ./a.out ac: In function 'main': ac:6: warning: dereferencing pointer 'p' does break ssortingct-aliasing rules ac: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 peut ne pas le remarquer. Puisque p a un type différent de &x , changer ce que les points p ne peuvent pas affecter ce que &x pointe vers (en dehors de certaines exceptions bien définies). Par conséquent, le compilateur est libre de conserver la valeur de x dans un registre et de ne pas mettre à jour ce registre en tant que *p . Le programme élimine deux pointeurs vers la même adresse et obtient deux valeurs différentes!

La morale de cet exemple est que penser à un pointeur (non valide) comme adresse est correct, tant que vous restz dans 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 qu’il est difficile d’obtenir un sentiment intuitif à moins de savoir ce qui se passe sous le capot. Et ce qui se passe sous le capot, c’est que le lien entre les pointeurs et les adresses est un peu flou, à la fois pour supporter des architectures de processeurs «exotiques» et pour prendre en charge l’optimisation des compilateurs.

Alors, pensez aux pointeurs comme des adresses comme un premier pas dans votre compréhension, mais ne suivez pas cette intuition trop loin.

Un pointeur est une variable qui HOLDS adresse mémoire, pas 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 si simple.

entrer la description de l'image ici

Un programme pour démontrer ce que je dis et sa sortie est ici:

http://ideone.com/rcSUsb

Le programme:

 #include  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; } 

Il est difficile de dire exactement ce que les auteurs de ces livres veulent dire exactement. Si un pointeur contient une adresse ou non, cela dépend de la façon dont vous définissez une adresse et de la façon dont vous définissez un pointeur.

A en juger par toutes les réponses écrites, certaines personnes supposent que (1) une adresse doit être un entier et (2) un pointeur ne doit pas nécessairement être dit virtuel dans la spécification. Avec ces hypothèses, alors clairement les indicateurs ne contiennent pas nécessairement des adresses.

Cependant, nous voyons que tandis que (2) est probablement vrai, (1) ne doit probablement pas être vrai. Et que faire du fait que le & est appelé l’ adresse de l’ opérateur selon la réponse de @ CornStalks? Est-ce que cela signifie que les auteurs de la spécification ont l’intention qu’un pointeur contienne une adresse?

Alors, pouvons-nous dire, le pointeur contient une adresse, mais une adresse ne doit pas nécessairement être un entier? Peut être.

Je pense que tout cela est un discours sémantique pédant. C’est totalement inutile. Pouvez-vous penser à un compilateur qui génère du code de telle manière que la valeur d’un pointeur ne soit pas une adresse? Si oui, quoi? C’est ce que je pensais…

Je pense que l’auteur du livre (le premier extrait qui prétend que les pointeurs ne sont pas simplement des adresses) se réfère probablement au fait qu’un pointeur est accompagné des informations de type inhérent.

Par exemple,

  int x; int* y = &x; char* z = &x; 

y et z sont tous deux des pointeurs, mais y + 1 et z + 1 sont différents. si ce sont des adresses de mémoire, ces expressions ne vous donneraient-elles pas la même valeur?

Et ici, la reflection sur les pointeurs, comme s’il s’agissait d’adresses, conduit généralement à la peine . Des bogues ont été écrits parce que les gens pensent aux pointeurs comme s’ils étaient des adresses , ce qui conduit généralement à des peines .

55555 n’est probablement pas un pointeur, bien qu’il puisse s’agir d’une adresse, mais (int *) 55555 est un pointeur. 55555 + 1 = 55556, mais (int *) 55555 + 1 est 55559 (différence +/- en termes de taille de (int)).

Un pointeur est une abstraction représentant un emplacement mémoire. Notez que la citation ne dit pas que penser aux pointeurs comme s’il s’agissait d’adresses de mémoire est faux, cela dit simplement que cela “conduit généralement à la peine”. En d’autres termes, cela vous amène à avoir des attentes incorrectes.

La source de chagrin la plus probable est certainement l’ arithmétique du pointeur, qui est en fait l’une des forces de C. Si un pointeur était une adresse, vous vous attendriez à ce que l’arithmétique du pointeur soit une arithmétique d’adresse; mais ce n’est pas. Par exemple, l’ajout de 10 à une adresse devrait vous donner une adresse plus grande de 10 unités d’adressage; mais l’ajout de 10 à un pointeur l’incrémente de 10 fois la taille du type d’object sur lequel il pointe (et même pas la taille réelle, mais arrondi à une limite d’alignement). Avec un int * sur une architecture ordinaire avec des nombres entiers de 32 bits, l’append à 10 l’incrémenterait de 40 unités d’adressage (octets). Les programmeurs C expérimentés sont conscients de cela et vivent avec elle, mais votre auteur n’est évidemment pas fan des métaphores négligées.

Il y a la question supplémentaire de savoir comment le contenu du pointeur représente l’emplacement de mémoire: Comme beaucoup de réponses l’ont expliqué, une adresse n’est pas toujours un int (ou long). Dans certaines architectures, une adresse est un “segment” plus un décalage. Un pointeur pourrait même ne contenir que le décalage dans le segment en cours (pointeur “proche”), ce qui en soi n’est pas une adresse mémoire unique. Et le contenu du pointeur ne peut avoir qu’une relation indirecte avec une adresse mémoire lorsque le matériel le comprend. Mais l’auteur de la citation citée ne mentionne même pas la représentation, alors je pense que c’était plutôt l’équivalence conceptuelle que la représentation.

Voici comment je l’ai expliqué à des personnes confuses dans le passé: Un pointeur a deux atsortingbuts qui affectent son comportement. Il a une valeur , qui est (dans les environnements typiques) une adresse mémoire, et un type , qui vous indique le type et la taille de l’object qu’il pointe.

Par exemple, donné:

 union { int i; char c; } u; 

Vous pouvez avoir trois pointeurs différents pointant tous vers ce même object:

 void *v = &u; int *i = &u.i; char *c = &u.c; 

Si vous comparez les valeurs de ces pointeurs, ils sont tous égaux:

 v==i && i==c 

Cependant, si vous incrémentez chaque pointeur, vous verrez que le type sur lequel ils pointent devient pertinent.

 i++; c++; // You can't perform arithmetic on a void pointer, so no v++ i != c 

Les variables i et c auront des valeurs différentes à ce stade, car i++ oblige i à contenir l’adresse du nombre entier accessible suivant, et c++ fait en sorte que c pointe sur le caractère adressable suivant. Typiquement, les entiers occupent plus de mémoire que les caractères, donc i finirai par avoir une valeur plus grande que c après les avoir tous deux incrémentés.

Mark Bessey l’a déjà dit, mais il faut insister sur ce point jusqu’à ce qu’il soit compris.

Le pointeur a autant à voir avec une variable qu’un littéral 3.

Le pointeur est un tuple d’une valeur (d’une adresse) et d’un type (avec des propriétés supplémentaires, telles que lecture seule). Le type (et les parameters supplémentaires, le cas échéant) peuvent définir ou restreindre davantage le contexte. par exemple. __far ptr, __near ptr : quel est le contexte de l’adresse: stack, tas, adresse linéaire, décalage de quelque part, mémoire physique ou quoi.

C’est la propriété de type qui rend l’arithmétique du pointeur un peu différente de l’arithmétique entière.

Les contre-exemples d’un pointeur de ne pas être une variable sont trop nombreux pour être ignorés

  • fopen retourne un pointeur FILE. (où est la variable)
  • pointeur de stack ou pointeur de trame étant généralement des registres non adressables

    *(int *)0x1231330 = 13; – convertir un nombre entier arbitraire en un type pointer_of_integer et écrire / lire un entier sans jamais introduire de variable

Pendant la durée de vie d’un programme C, il y aura beaucoup d’autres instances de pointeurs temporaires qui n’ont pas d’adresse – et donc ce ne sont pas des variables, mais des expressions / valeurs avec un type associé à la compilation.

Tu as raison et sain d’esprit. Normalement, un pointeur est juste une adresse, vous pouvez donc le convertir en entier et faire des calculs.

Mais parfois, les pointeurs ne sont qu’une partie d’une adresse. Sur certaines architectures, un pointeur est converti en adresse avec ajout de base ou un autre registre CPU est utilisé.

Mais ces jours-ci, sur une architecture PC et ARM avec un modèle de mémoire plat et un langage C compilés en natif, il est acceptable de penser qu’un pointeur est une adresse entière à un endroit de la RAM adressable unidimensionnelle.

Un pointeur, comme toute autre variable de C, est fondamentalement une collection de bits pouvant être représentée par une ou plusieurs valeurs de caractère unsigned char concaténées (comme avec tout autre type de caractère cariable, sizeof(some_variable) indiquera le nombre de valeurs de caractère unsigned char ) . Ce qui rend un pointeur différent des autres variables, c’est qu’un compilateur C interprétera les bits d’un pointeur comme identifiant, d’une manière ou d’une autre, un endroit où une variable peut être stockée. Dans C, contrairement à d’autres langages, il est possible de demander de l’espace pour plusieurs variables, puis de convertir un pointeur en n’importe quelle valeur de cet ensemble en un pointeur vers toute autre variable de cet ensemble.

De nombreux compilateurs implémentent des pointeurs en utilisant leurs adresses de machine réelles, mais ce n’est pas la seule implémentation possible. Une implémentation peut conserver un tableau – non accessible au code utilisateur – listant l’adresse matérielle et la taille allouée de tous les objects mémoire (ensembles de variables) utilisés par un programme, et faire en sorte que chaque pointeur contienne un index dans un tableau avec un décalage de cet index. Une telle conception permettrait à un système non seulement de restreindre le code au seul fonctionnement de la mémoire qu’il possédait, mais aussi de garantir qu’un pointeur vers un élément de mémoire ne pourrait pas être converti accidentellement en un pointeur vers un autre élément de mémoire adresses, si foo et bar sont des tableaux de 10 éléments stockés consécutivement dans la mémoire, un pointeur sur l’élément “onzième” de foo pourrait plutôt pointer vers le premier élément de la bar , mais dans un système où chaque “pointeur” est un object ID et un décalage, le système pourrait intercepter si le code tentait d’indexer un pointeur sur foo au-delà de sa plage allouée). It would also be possible for such a system to eliminate memory-fragmentation problems, since the physical addresses associated with any pointers could be moved around.

Note that while pointers are somewhat abstract, they’re not quite abstract enough to allow a fully-standards-compliant C comstackr to implement a garbage collector. The C comstackr specifies that every variable, including pointers, is represented as a sequence of unsigned char values. Given any variable, one can decompose it into a sequence of numbers and later convert that sequence of numbers back into a variable of the original type. Consequently, it would be possible for a program to calloc some storage (receiving a pointer to it), store something there, decompose the pointer into a series of bytes, display those on the screen, and then erase all reference to them. If the program then accepted some numbers from the keyboard, reconstituted those to a pointer, and then sortinged to read data from that pointer, and if user entered the same numbers that the program had earlier displayed, the program would be required to output the data that had been stored in the calloc ‘ed memory. Since there is no conceivable way the computer could know whether the user had made a copy of the numbers that were displayed, there would be no conceivable may the computer could know whether the aforementioned memory might ever be accessed in future.

A pointer is a variable type that is natively available in C/C++ and contains a memory address. Like any other variable it has an address of its own and takes up memory (the amount is platform specific).

One problem you will see as a result of the confusion is trying to change the referent within a function by simply passing the pointer by value. This will make a copy of the pointer at function scope and any changes to where this new pointer “points” will not change the referent of the pointer at the scope that invoked the function. In order to modify the actual pointer within a function one would normally pass a pointer to a pointer.

BRIEF SUMMARY (which I will also put at the top):

(0) Thinking of pointers as addresses is often a good learning tool, and is often the actual implementation for pointers to ordinary data types.

(1) But on many, perhaps most, comstackrs pointers to functions are not addresses, but are bigger than an address (typically 2x, sometimes more), or are actually pointers to a struct in memory than contains the addresses of function and stuff like a constant pool.

(2) Pointers to data members and pointers to methods are often even stranger.

(3) Legacy x86 code with FAR and NEAR pointer issues

(4) Several examples, most notably the IBM AS/400, with secure “fat pointers”.

I am sure you can find more.

DETAIL:

UMMPPHHH!!!!! Many of the answers so far are fairly typical “programmer weenie” answers – but not comstackr weenie or hardware weenie. Since I pretend to be a hardware weenie, and often work with comstackr weenies, let me throw in my two cents:

On many, probably most, C comstackrs, a pointer to data of type T is, in fact, the address of T .

Bien.

But, even on many of these comstackrs, certain pointers are NOT addresses. You can tell this by looking at sizeof(ThePointer) .

For example, pointers to functions are sometimes quite a lot bigger than ordinary addresses. Or, they may involve a level of indirection. This article provides one description, involving the Intel Itanium processor, but I have seen others. Typically, to call a function you must know not only the address of the function code, but also the address of the function’s constant pool – a region of memory from which constants are loaded with a single load instruction, rather than the comstackr having to generate a 64 bit constant out of several Load Immediate and Shift and OR instructions. So, rather than a single 64 bit address, you need 2 64 bit addresses. Some ABIs (Application Binary Interfaces) move this around as 128 bits, whereas others use a level of indirection, with the function pointer actually being the address of a function descriptor that contains the 2 actual addresses just mentioned. Ce qui est mieux? Depends on your point of view: performance, code size, and some compatibility issues – often code assumes that a pointer can be cast to a long or a long long, but may also assume that the long long is exactly 64 bits. Such code may not be standards compliant, but nevertheless customers may want it to work.

Many of us have painful memories of the old Intel x86 segmented architecture, with NEAR POINTERs and FAR POINTERS. Thankfully these are nearly extinct by now, so only a quick summary: in 16 bit real mode, the actual linear address was

 LinearAddress = SegmentRegister[SegNum].base << 4 + Offset 

Whereas in protected mode, it might be

 LinearAddress = SegmentRegister[SegNum].base + offset 

with the resulting address being checked against a limit set in the segment. Some programs used not really standard C/C++ FAR and NEAR pointer declarations, but many just said *T --- but there were comstackr and linker switches so, for example, code pointers might be near pointers, just a 32 bit offset against whatever is in the CS (Code Segment) register, while the data pointers might be FAR pointers, specifying both a 16 bit segment number and a 32 bit offset for a 48 bit value. Now, both of these quantities are certainly related to the address, but since they aren't the same size, which of them is the address? Moreover, the segments also carried permissions - read-only, read-write, executable - in addition to stuff related to the actual address.

A more interesting example, IMHO, is (or, perhaps, was) the IBM AS/400 family. This computer was one of the first to implement an OS in C++. Pointers on this machime were typically 2X the actual address size - eg as this presentation says, 128 bit pointers, but the actual addresses were 48-64 bits, and, again, some extra info, what is called a capability, that provided permissions such as read, write, as well as a limit to prevent buffer overflow. Yes: you can do this compatibly with C/C++ -- and if this were ubiquitous, the Chinese PLA and slavic mafia would not be hacking into so many Western computer systems. But historically most C/C++ programming has neglected security for performance. Most interestingly, the AS400 family allowed the operating system to create secure pointers, that could be given to unprivileged code, but which the unprivileged code could not forge or tamper with. Again, security, and while standards compliant, much sloppy non-standards compliant C/C++ code will not work in such a secure system. Again, there are official standards, and there are de-facto standards.

Now, I'll get off my security soapbox, and mention some other ways in which pointers (of various types) are often not really addresses: Pointers to data members, pointers to member functions methods, and the static versions thereof are bigger than an ordinary address. As this post says:

There are many ways of solving this [problems related to single versus multiple inheitance, and virtual inheritance]. Here's how the Visual Studio comstackr decides to handle it: A pointer to a member function of a multiply-inherited class is really a structure." And they go on to say "Casting a function pointer can change its size!".

As you can probably guess from my pontificating on (in)security, I've been involved in C/C++ hardware/software projects where a pointer was treated more like a capability than a raw address.

I could go on, but I hope you get the idea.

BRIEF SUMMARY (which I will also put at the top):

(0) thinking of pointers as addresses is often a good learning tool, and is often the actual implementation for pointers to ordinary data types.

(1) But on many, perhaps most, comstackrs pointers to functions are not addresses, but are bigger than an address (typically 2X, sometimes more), or are actually pointers to a struct in memory than contains the addresses of function and stuff like a constant pool.

(2) Pointers to data members and pointers to methods are often even stranger.

(3) Legacy x86 code with FAR and NEAR pointer issues

(4) Several examples, most notably the IBM AS/400, with secure "fat pointers".

I am sure you can find more.

A pointer is just another variable which is used to hold the address of a memory location (usually the memory address of another variable).

You can see it this way. A pointer is a value that represents an address in the addressable memory space.

A pointer is just another variable that can contain memory address usually of another variable. A pointer being a variable it too has an memory address.

AC pointer is very similar to a memory address but with machine-dependent details abstracted away, as well as some features not found in the lower level instruction set.

For example, a C pointer is relatively richly typed. If you increment a pointer through an array of structures, it nicely jumps from one structure to the other.

Pointers are subject to conversion rules and provide comstack time type checking.

There is a special “null pointer” value which is portable at the source code level, but whose representation may differ. If you assign an integer constant whose value is zero to a pointer, that pointer takes on the null pointer value. Ditto if you initialize a pointer that way.

A pointer can be used as a boolean variable: it tests true if it is other than null, and false if it is null.

In a machine language, if the null pointer is a funny address like 0xFFFFFFFF, then you may have to have explicit tests for that value. C hides that from you. Even if the null pointer is 0xFFFFFFFF, you can test it using if (ptr != 0) { /* not null! */} .

Uses of pointers which subvert the type system lead to undefined behavior, whereas similar code in machine language might be well defined. Assemblers will assemble the instructions you have written, but C comstackrs will optimize based on the assumption that you haven’t done anything wrong. If a float *p pointer points to a long n variable, and *p = 0.0 is executed, the comstackr is not required to handle this. A subsequent use of n will not necessary read the bit pattern of the float value, but perhaps, it will be an optimized access which is based on the “ssortingct aliasing” assumption that n has not been touched! That is, the assumption that the program is well-behaved, and so p should not be pointing at n .

In C, pointers to code and pointers to data are different, but on many architectures, the addresses are the same. C comstackrs can be developed which have “fat” pointers, even though the target architecture does not. Fat pointers means that pointers are not just machine addresses, but contain other info, such as information about the size of the object being pointed at, for bounds checking. Portably written programs will easily port to such comstackrs.

So you can see, there are many semantic differences between machine addresses and C pointers.

Before understanding pointers we need to understand objects. Objects are entities which exist and has a location specifier called an address. A pointer is just a variable like any other variables in C with a type called pointer whose content is interpreted as the address of an object which supports the following operation.

 + : A variable of type integer (usually called offset) can be added to yield a new pointer - : A variable of type integer (usually called offset) can be subtracted to yield a new pointer : A variable of type pointer can be subtracted to yield an integer (usually called offset) * : De-referencing. Resortingeve the value of the variable (called address) and map to the object the address refers to. ++: It's just `+= 1` --: It's just `-= 1` 

A pointer is classified based on the type of object it is currently referring. The only part of the information it matters is the size of the object.

Any object supports an operation, & (address of), which resortingeves the location specifier (address) of the object as a pointer object type. This should abate the confusion surrounding the nomenclature as this would make sense to call & as an operation of an object rather than a pointer whose resultant type is a pointer of the object type.

Note Throughout this explanation, I have left out the concept of memory.

An address is used to identify a piece of fixed-size storage, usually for each bytes, as an integer. This is precisely called as byte address , which is also used by the ISO C. There can be some other methods to construct an address, eg for each bit. However, only byte address is so often used, we usually omit “byte”.

Technically, an address is never a value in C, because the definition of term “value” in (ISO) C is:

precise meaning of the contents of an object when interpreted as having a specific type

(Emphasized by me.) However, there is no such “address type” in C.

Pointer is not the same. Pointer is a kind of type in the C language. There are several distinct pointer types. They does not necessarily obey to identical set of rules of the language, eg the effect of ++ on a value of type int* vs. char* .

A value in C can be of a pointer type. This is called a pointer value . To be clear, a pointer value is not a pointer in the C language. But we are accustomed to mix them together, because in C it is not likely to be ambiguous: if we call an expression p as a “pointer”, it is merely a pointer value but not a type, since a named type in C is not expressed by an expression , but by a type-name or a typedef-name .

Some other things are subtle. As a C user, firstly, one should know what object means:

region of data storage in the execution environment, the contents of which can represent values

An object is an entity to represent values, which are of a specific type. A pointer is an object type . So if we declare int* p; , then p means “an object of pointer type”, or an “pointer object”.

Note there is no “variable” normatively defined by the standard (in fact it is never being used as a noun by ISO C in normative text). However, informally, we call an object a variable, as some other language does. (But still not so exactly, eg in C++ a variable can be of reference type normatively, which is not an object.) The phrases “pointer object” or “pointer variable” are sometimes treated like “pointer value” as above, with a probable slight difference. (One more set of examples is “array”.)

Since pointer is a type, and address is effectively “typeless” in C, a pointer value roughly “contains” an address. And an expression of pointer type can yield an address, eg

ISO C11 6.5.2.3

3 The unary & operator yields the address of its operand.

Note this wording is introduced by WG14/N1256, ie ISO C99:TC3. In C99 there is

3 The unary & operator returns the address of its operand.

It reflects the committee’s opinion: an address is not a pointer value returned by the unary & operator.

Despite the wording above, there are still some mess even in the standards.

ISO C11 6.6

9 An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator

ISO C++11 5.19

3 … An address constant expression is a prvalue core constant expression of pointer type that evaluates to the address of an object with static storage duration, to the address of a function, or to a null pointer value, or a prvalue core constant expression of type std::nullptr_t . …

(Recent C++ standard draft uses another wording so there is no this problem.)

Actually both “address constant” in C and “address constant expression” in C++ are constant expression of pointer types (or at least “pointer-like” types since C++11).

And the builtin unary & operator is called as “address-of” in C and C++; similarily, std::addressof is introduced in C++11.

These naming may bring misconception. The resulted expression is of pointer type, so they’d be interpreted as: the result contains/yields an address, rather than is an address.

It says “because it confuses those who don’t know what addresses are about” – also, it’s true: if you learn what addresses are about, you’ll be not confused. Theoretically, pointer is a variable which points to another, practically holds an address, which is the address of the variable it points to. I don’t know why should hide this fact, it’s not a rocket science. If you understand pointers, you’ll one step closer to understand how computers work. Go ahead!

Come to think about it, I think it’s a matter of semantics. I don’t think the author is right, since the C standard refers to a pointer as holding an address to the referenced object as others have already mentioned here. However, address!=memory address. An address can be really anything as per C standard although it will eventually lead to a memory address, the pointer itself can be an id, an offset + selector (x86), really anything as long as it can describe (after mapping) any memory address in the addressable space.

One other way in which a C or C++ pointer differs from a simple memory address due to the different pointer types I haven’t seen in the other answers (altrhough given their total size, I may have overlooked it). But it is probably the most important one, because even experienced C/C++ programmers can sortingp over it:

The comstackr may assume that pointers of incompatible types do not point to the same address even if they clearly do, which may give behaviour that would no be possible with a simple pointer==address model. Consider the following code (assuming sizeof(int) = 2*sizeof(short) ):

 unsigned int i = 0; unsigned short* p = (unsigned short*)&i; p[0]=p[1]=1; if (i == 2 + (unsigned short)(-1)) { // you'd expect this to execute, but it need not } if (i == 0) { // you'd expect this not to execute, but it actually may do so } 

Note that there’s an exception for char* , so manipulating values using char* is possible (although not very portable).

Quick summary: AC address is a value, typically represented as a machine-level memory address, with a specific type.

The unqualified word “pointer” is ambiguous. C has pointer objects (variables), pointer types , pointer expressions , and pointer values .

It’s very common to use the word “pointer” to mean “pointer object”, and that can lead to some confusion — which is why I try to use “pointer” as an adjective rather than as a noun.

The C standard, at least in some cases, uses the word “pointer” to mean “pointer value”. For example, the description of malloc says it “returns either a null pointer or a pointer to the allocated space”.

So what’s an address in C? It’s a pointer value, ie, a value of some particular pointer type. (Except that a null pointer value is not necessarily referred to as an “address”, since it isn’t the address of anything).

The standard’s description of the unary & operator says it “yields the address of its operand”. Outside the C standard, the word “address” is commonly used to refer to a (physical or virtual) memory address, typically one word in size (whatever a “word” is on a given system).

AC “address” is typically implemented as a machine address — just as a C int value is typically implemented as a machine word. But a C address (pointer value) is more than just a machine address. It’s a value typically represented as a machine address, and it’s a value with some specific type .

A pointer value is an address. A pointer variable is an object that can store an address. This is true because that’s what the standard defines a pointer to be. It’s important to tell it to C novices because C novices are often unclear on the difference between a pointer and the thing it points to (that is to say, they don’t know the difference between an envelope and a building). The notion of an address (every object has an address and that’s what a pointer stores) is important because it sorts that out.

However, the standard talks at a particular level of abstraction. Those people the author talks about who “know what addresses are about”, but who are new to C, must necessarily have learned about addresses at a different level of abstraction — perhaps by programming assembly language. There is no guarantee that the C implementation uses the same representation for addresses as the CPUs opcodes use (referred to as “the store address” in this passage), that these people already know about.

He goes on to talk about “perfectly reasonable address manipulation”. As far as the C standard is concerned there’s basically no such thing as “perfectly reasonable address manipulation”. Addition is defined on pointers and that is basically it. Sure, you can convert a pointer to integer, do some bitwise or arithmetic ops, and then convert it back. This is not guaranteed to work by the standard, so before writing that code you’d better know how your particular C implementation represents pointers and performs that conversion. It probably uses the address representation you expect, but it it doesn’t that’s your fault because you didn’t read the manual. That’s not confusion, it’s incorrect programming procedure 😉

In short, C uses a more abstract concept of an address than the author does.

The author’s concept of an address of course is also not the lowest-level word on the matter. What with virtual memory maps and physical RAM addressing across multiple chips, the number that you tell the CPU is “the store address” you want to access has basically nothing to do with where the data you want is actually located in hardware. It’s all layers of indirection and representation, but the author has chosen one to privilege. If you’re going to do that when talking about C, choose the C level to privilege !

Personally I don’t think the author’s remarks are all that helpful, except in the context of introducing C to assembly programmers. It’s certainly not helpful to those coming from higher level languages to say that pointer values aren’t addresses. It would be far better to acknowledge the complexity than it is to say that the CPU has the monopoly on saying what an address is and thus that C pointer values “are not” addresses. They are addresses, but they may be written in a different language from the addresses he means. Distinguishing the two things in the context of C as “address” and “store address” would be adequate, I think.

Simply to say pointers are actually offset part of the segmentation mechanism which translate to Linear Address after segmentation and then to Physical address after paging. Physical Addresses are actually addressed from you ram.

  Selector +--------------+ +-----------+ ---------->| | | | | Segmentation | ------->| Paging | Offset | Mechanism | | Mechanism | ---------->| | | | +--------------+ +-----------+ Virtual Linear Physical