Quel est le «type» de données que les pointeurs contiennent en langage C?

30

Je sais que les pointeurs contiennent des adresses. Je sais que les types de pointeurs sont "généralement" connus en fonction du "type" de données vers lesquelles ils pointent. Mais, les pointeurs sont toujours des variables et les adresses qu'ils détiennent doivent avoir un "type" de données. Selon mes informations, les adresses sont au format hexadécimal. Mais, je ne sais toujours pas quel "type" de données est cet hexadécimal. (Notez que je sais ce qu'est un hexadécimal, mais quand vous dites 10CBA20, par exemple, cette chaîne de caractères? Des entiers? Quoi? Quand je veux accéder à l'adresse et la manipuler .. elle-même, j'ai besoin de connaître son type. c'est pourquoi je demande.)

Gold_Sky
la source
17
Les pointeurs ne sont pas des variables , mais des valeurs . Les variables contiennent des valeurs (et si leur type est un type pointeur, cette valeur est un pointeur et peut être l'adresse d'une zone mémoire contenant quelque chose de significatif). Une zone de mémoire donnée peut être utilisée pour contenir différentes valeurs de différents types.
Basile Starynkevitch du
29
"les adresses sont au format hexadécimal" Non, c'est juste le débogueur ou une bibliothèque de bits de formatage. Avec le même argument, on pourrait dire qu'ils sont en binaire ou en octal.
usr
Vous feriez mieux de poser des questions sur le format , pas sur le type . D'où quelques réponses hors piste ci-dessous ... (même si Kilian's est parfait).
Courses de légèreté avec Monica
1
Je pense que la question la plus profonde ici est la compréhension du type par OP . En fin de compte, les valeurs que vous manipulez dans votre programme ne sont que des bits en mémoire. Les types sont la manière du programmeur de dire au compilateur comment traiter ces bits lorsqu'il génère du code assembleur.
Justin Lardinois
Je suppose qu'il est trop tard pour l'éditer avec toutes ces réponses, mais cette question aurait été meilleure si vous aviez restreint le matériel et / ou le système d'exploitation, par exemple "sous x64 Linux".
hyde

Réponses:

64

Le type d'une variable de pointeur est .. pointeur.

Les opérations que vous êtes formellement autorisé à faire en C sont de le comparer (à d'autres pointeurs, ou à la valeur spéciale NULL / zéro), d'ajouter ou de soustraire des entiers, ou de le convertir en d'autres pointeurs.

Une fois que vous avez accepté un comportement indéfini , vous pouvez voir quelle est réellement la valeur. Ce sera généralement un mot machine, le même genre de chose qu'un entier, et peut généralement être casté sans perte vers et depuis un type entier. (Beaucoup de code Windows le fait en masquant les pointeurs dans les typedefs DWORD ou HANDLE).

Il existe des architectures où les pointeurs ne sont pas simples car la mémoire n'est pas plate. DOS / 8086 «près» et «loin»; Différents espaces mémoire et code de PIC.

pjc50
la source
2
Vous êtes également autorisé à prendre la différence entre deux pointeurs p1-p2. Le résultat est une valeur intégrale signée. En particulier,&(array[i])-&(array[j]) == i-j
MSalters
13
En fait, la conversion en un type intégral est également spécifiée, spécifiquement vers intptr_tet uintptr_tdont la garantie est «suffisamment grande» pour les valeurs de pointeur.
Matthieu M.
3
Vous pouvez dépendre de la conversion pour fonctionner, mais le mappage entre les entiers et les pointeurs est défini par l'implémentation. (La seule exception est 0 -> null, et même cela n'est spécifié que si le 0 est un IIRC constant.)
cHao
7
L'ajout du pspécificateur à printf permet d'obtenir une représentation lisible par l'homme d'un pointeur vide un comportement défini, si la mise en œuvre dépend de c.
dmckee
6
Cette réponse a la bonne idée, mais échoue sur les revendications spécifiques. La contrainte d'un pointeur sur un type intégral n'est pas un comportement indéfini et les types de données Windows HANDLE ne sont pas des valeurs de pointeur (ce ne sont pas des pointeurs masqués dans les types de données intégraux, ce sont des entiers masqués dans les types de pointeurs, pour éviter l'arithmétique).
Ben Voigt
44

Vous compliquez trop les choses.

Les adresses ne sont que des entiers, point final. Idéalement, il s'agit du numéro de la cellule de mémoire référencée (en pratique, cela devient plus compliqué à cause des segments, de la mémoire virtuelle, etc.).

La syntaxe hexadécimale est une fiction complète qui n'existe que pour la commodité des programmeurs. 0x1A et 26 sont exactement le même nombre d'exactement le même type , et ce n'est pas non plus ce que l'ordinateur utilise - en interne, l'ordinateur utilise toujours 00011010 (une série de signaux binaires).

Si un compilateur vous permet ou non de traiter les pointeurs comme des nombres dépend de la définition du langage - les langages de "programmation système" sont traditionnellement plus transparents sur la façon dont les choses fonctionnent sous le capot, tandis que les langages "de haut niveau" essaient plus souvent de cacher le métal nu du programmeur - mais cela ne change rien au fait que les pointeurs sont des nombres, et généralement le type de nombre le plus courant (celui avec autant de bits que votre architecture de processeur).

Kilian Foth
la source
26
Les adresses ne sont certainement pas uniquement des entiers. Tout comme les nombres à virgule flottante ne sont certainement pas uniquement des entiers.
gnasher729
8
Effectivement. Le contre-exemple le plus connu est l'Intel 8086, où les pointeurs sont deux entiers.
MSalters
5
@Rob Dans un modèle de mémoire segmentée, un pointeur peut être soit une valeur unique (une adresse par rapport au début du segment; un décalage) avec le segment impliqué, soit une paire segment / sélecteur et décalage . (Je pense qu'Intel a utilisé le terme "sélecteur"; je suis trop paresseux pour le rechercher.) Sur le 8086, ceux-ci étaient représentés comme deux entiers 16 bits, qui se combinaient pour former une adresse physique 20 bits. (Oui, vous pourriez adresser la même cellule de mémoire de nombreuses façons différentes, si vous étiez si enclin: adresse = (segment << 4 + décalage) & 0xfffff.) Cela a été appliqué à tous les compatibles x86 lors de l'exécution en mode réel.
un CVn le
4
En tant que programmeur assembleur à long terme, je peux attester que la mémoire de l'ordinateur n'est rien d'autre que des emplacements de mémoire contenant des entiers. Cependant, c'est la façon dont vous les traitez et gardez une trace de ce que ces entiers représentent qui est important. Par exemple, sur mon système, le nombre décimal 4075876853 est stocké sous la forme x'F2F0F1F5 ', qui est la chaîne' 2015 'dans EBCDIC. Decimal 2015 serait stocké en tant que 000007DF tandis que x'0002015C 'représente décimal 2015 au format décimal compressé. En tant que programmeur assembleur, vous devez en garder la trace; le compilateur le fait pour les langages HL.
Steve Ives
7
Les adresses peuvent être mises en correspondance biunivoque avec des nombres entiers, mais il en va de même pour tout le reste sur un ordinateur :)
hobbs
16

Un pointeur est juste cela - un pointeur. Ce n'est pas autre chose. N'essayez pas de penser que c'est autre chose.

Dans les langages comme C, C ++ et Objective-C, les pointeurs de données ont quatre types de valeurs possibles:

  1. Un pointeur peut être l'adresse d'un objet.
  2. Un pointeur peut pointer juste après le dernier élément d'un tableau.
  3. Un pointeur peut être un pointeur nul, ce qui signifie qu'il ne pointe vers rien.
  4. Un pointeur peut avoir une valeur indéterminée, en d'autres termes, ce sont des déchets, et tout peut arriver (y compris de mauvaises choses) si vous essayez de l'utiliser.

Il existe également des pointeurs de fonction, qui identifient une fonction, ou sont des pointeurs de fonction nuls, ou ont une valeur indéterminée.

D'autres pointeurs sont "pointeur vers membre" en C ++. Ce ne sont certainement pas des adresses mémoire! Au lieu de cela, ils identifient un membre de n'importe quelle instance d'une classe. Dans Objective-C, vous avez des sélecteurs, qui sont quelque chose comme "pointeur vers une méthode d'instance avec un nom de méthode et des noms d'argument donnés". Comme un pointeur de membre, il identifie tous méthodes de toutes les classes tant qu'elles se ressemblent.

Vous pouvez étudier comment un compilateur spécifique implémente des pointeurs, mais c'est une question entièrement différente.

gnasher729
la source
4
Il existe des pointeurs vers les fonctions et, en C ++, des pointeurs vers les membres.
sdenham
Les pointeurs C ++ vers les membres ne sont pas des adresses mémoire? Bien sûr qu'ils le sont. class A { public: int num; int x; }; int A::*pmi = &A::num; A a; int n = a.*pmi;La variable pmine serait pas très utile si elle ne contenait pas d'adresse mémoire, à savoir, comme la dernière ligne du code l'établit, l'adresse du membre numd'instance ade classe A. Vous pouvez convertir ceci en un intpointeur ordinaire (bien que le compilateur vous donnerait probablement un avertissement) et le déréférencer avec succès (prouvant qu'il s'agit du sucre syntaxique pour tout autre pointeur).
dodgethesteamroller
9

Un pointeur est un modèle de bits adressant (identifiant de manière unique à des fins de lecture ou d'écriture) un mot de stockage dans la RAM. Pour des raisons historiques et conventionnelles, l'unité de mise à jour est de huit bits, connus en anglais comme un "octet" ou en français, plutôt plus logiquement, comme un octet. C'est omniprésent mais pas inhérent; d'autres tailles ont existé.

Si je me souviens bien, il y avait un ordinateur qui utilisait un mot de 29 bits; non seulement ce n'est pas une puissance de deux, mais c'est même le premier. Je pensais que c'était SILLIAC mais l'article pertinent de Wikipedia ne le supporte pas. CAN BUS utilise des adresses 29 bits mais par convention, les adresses réseau ne sont pas appelées pointeurs même lorsqu'elles sont fonctionnellement identiques.

Les gens continuent d'affirmer que les pointeurs sont des entiers. Ce n'est ni intrinsèque ni essentiel, mais si nous interprétons les modèles de bits comme des entiers, la qualité utile de l'ordinalité émerge, permettant une implémentation très directe (et donc efficace sur un petit matériel) de constructions comme "string" et "array". La notion de mémoire contiguë dépend de la contiguïté ordinale et un positionnement relatif est possible; la comparaison d'entiers et les opérations arithmétiques peuvent être appliquées de manière significative. Pour cette raison, il existe presque toujours une forte corrélation entre la taille des mots pour l'adressage de stockage et l'ALU (la chose qui fait des mathématiques entières).

Parfois, les deux ne correspondent pas. Dans les premiers PC, le bus d'adresse avait une largeur de 24 bits.

Peter Wone
la source
Nitpick, de nos jours dans les systèmes d'exploitation courants, le pointeur identifie l'emplacement dans la mémoire virtuelle et n'a rien à voir directement avec un mot de RAM physique (l'emplacement de la mémoire virtuelle peut même ne pas exister physiquement s'il se trouve dans une page de mémoire connue pour être entièrement à zéro par le système d'exploitation). ).
hyde
@hyde - Votre argument a du mérite dans le contexte dans lequel vous l'avez évidemment voulu, mais la forme dominante de l'ordinateur est le contrôleur intégré, où les merveilles comme la mémoire virtuelle sont des innovations récentes avec un déploiement limité. De plus, ce que vous avez souligné ne permet en aucun cas au PO de comprendre les pointeurs. Je pensais qu'un contexte historique rendrait tout cela beaucoup moins arbitraire.
Peter Wone
Je ne sais pas si parler de RAM aidera OP à comprendre, car la question porte précisément sur ce que sont réellement les pointeurs . Oh, un autre nitpick, dans le pointeur c par définition, pointe vers l'octet (peut être converti en toute sécurité, par char*exemple, à des fins de copie / comparaison de mémoire, et sizeof char==1comme défini par la norme C), pas de mot (sauf si la taille du mot CPU est identique à la taille de l'octet).
hyde
Les pointeurs sont fondamentalement des clés de hachage pour le stockage. C'est invariant de langue et de plate-forme.
Peter Wone
La question concerne les pointeurs c . Et les pointeurs ne sont certainement pas des clés de hachage, car il n'y a pas de table de hachage, pas d'algorithme de hachage. Il s'agit naturellement d'une sorte de clés de carte / dictionnaire (pour une définition suffisamment large de "carte"), mais pas de clés de hachage .
hyde
6

Fondamentalement, chaque ordinateur moderne est une machine à pousser les bits. Habituellement, il pousse des bits dans des grappes de données, appelées octets, mots, dwords ou qwords.

Un octet est composé de 8 bits, un mot 2 octets (ou 16 bits), un mot dword 2 (ou 32 bits) et un qword 2 mots (ou 64 bits). Ce ne sont pas les seuls moyens d'organiser les bits. Des manipulations sur 128 bits et 256 bits se produisent également, souvent dans les instructions SIMD.

Les instructions d'assemblage fonctionnent sur les registres et les adresses de mémoire fonctionnent généralement sous l'une des formes ci-dessus.

Les ALU (unités logiques arithmétiques) fonctionnent sur des paquets de bits comme s'ils représentaient des entiers (généralement le format Complément de Deux), et les FPU comme s'ils étaient des valeurs à virgule flottante (généralement de style IEEE 754 floatet double). Les autres parties agiront comme s'il s'agissait de données groupées de certains formats, caractères, entrées de table, instructions CPU ou adresses.

Sur un ordinateur 64 bits typique, des paquets de 8 octets (64 bits) sont des adresses. Nous affichons ces adresses de manière conventionnelle au format hexadécimal (comme 0xabcd1234cdef5678), mais ce n'est qu'un moyen simple pour les humains de lire les modèles de bits. Chaque octet (8 bits) est écrit en deux caractères hexadécimaux (de manière équivalente, chaque caractère hexadécimal - 0 à F - représente 4 bits).

Ce qui se passe réellement (pour un certain niveau de fait), c'est qu'il y a des bits, généralement stockés dans un registre ou stockés dans des emplacements adjacents dans une banque de mémoire, et nous essayons simplement de les décrire à un autre humain.

Suivre un pointeur consiste à demander au contrôleur mémoire de nous donner quelques données à cet endroit. Vous demandez généralement au contrôleur de mémoire un certain nombre d'octets à un certain emplacement (enfin, implicitement une plage d'emplacements, généralement contigus), et il est fourni via divers mécanismes dans lesquels je n'entrerai pas.

Le code spécifie généralement une destination pour les données à extraire - un registre, une autre adresse mémoire, etc. - et généralement c'est une mauvaise idée de charger des données à virgule flottante dans un registre en attendant un entier, ou vice versa.

Le type de données en C / C ++ est quelque chose dont le compilateur garde la trace et il change le code généré. Habituellement, il n'y a rien d'intrinsèque dans les données qui les rend réellement de n'importe quel type. Juste une collection de bits (arrangés en octets) qui sont manipulés de manière entière (ou flottante ou de type adresse) par le code.

Il y a des exceptions à cela. Il existe des architectures où certaines choses sont un type différent de bits. L'exemple le plus courant est les pages d'exécution protégées - alors que les instructions indiquant au CPU ce qu'il faut faire sont des bits, au moment de l'exécution, les pages (mémoire) contenant le code à exécuter sont marquées spécialement, ne peuvent pas être modifiées et vous ne pouvez pas exécuter les pages qui ne sont pas marquées comme pages d'exécution.

Il existe également des données en lecture seule (parfois stockées dans la ROM qui ne peuvent pas être écrites physiquement!), Des problèmes d'alignement (certains processeurs ne peuvent pas charger les doubles à partir de la mémoire à moins qu'ils ne soient alignés de manière particulière, ou des instructions SIMD qui nécessitent un certain alignement), et des myriades de autres bizarreries d'architecture.

Même le niveau de détail ci-dessus est un mensonge. Les ordinateurs ne poussent pas «vraiment» les bits, ils poussent vraiment les tensions et le courant. Ces tensions et courants ne font parfois pas ce qu'ils sont "censés" faire au niveau de l'abstraction des bits. Les puces sont conçues pour détecter la plupart de ces erreurs et les corriger sans que l'abstraction de niveau supérieur n'en soit consciente.

Même c'est un mensonge.

Chaque niveau d'abstraction masque celui ci-dessous et vous permet de penser à résoudre des problèmes sans avoir à garder à l'esprit les diagrammes de Feynman pour les imprimer "Hello World".

Donc, à un niveau d'honnêteté suffisant, les ordinateurs poussent les bits, et ces bits ont un sens par la façon dont ils sont utilisés.

Yakk
la source
3

Les gens ont beaucoup fait pour savoir si les pointeurs sont des entiers ou non. Il existe en fait des réponses à ces questions. Cependant, vous allez devoir faire un pas dans le pays du cahier des charges, ce qui n'est pas pour les âmes sensibles. Nous allons jeter un oeil à la spécification C, ISO / IEC 9899: TC2

6.3.2.3 Pointeurs

  1. Un 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 d'interruption.

  2. Tout type de pointeur peut être converti en un type entier. Sauf indication contraire, le résultat est défini par l'implémentation. Si le résultat ne peut pas être représenté dans le type entier, le comportement n'est pas défini. Le résultat n'a pas besoin d'être dans la plage de valeurs de tout type entier.

Maintenant, pour cela, vous allez avoir besoin de connaître certains termes courants des spécifications. "implémentation définie" signifie que chaque compilateur est autorisé à le définir différemment. En fait, un compilateur peut même le définir de différentes manières en fonction des paramètres de votre compilateur. Un comportement indéfini signifie que le compilateur est autorisé à faire absolument n'importe quoi, de donner une erreur de temps de compilation à des comportements inexplicables, à fonctionner parfaitement.

De cela, nous pouvons voir que la forme de stockage sous-jacente n'est pas spécifiée, à part qu'il peut y avoir une conversion en un type entier. À vrai dire, pratiquement tous les compilateurs sous le soleil représentent des pointeurs sous le capot en tant qu'adresses entières (avec une poignée de cas spéciaux où il pourrait être représenté comme 2 entiers au lieu de 1), mais la spécification autorise absolument tout, comme représenter adresses sous forme de chaîne de 10 caractères!

Si nous avançons rapidement hors de C et regardons la spécification C ++, nous obtenons un peu plus de clarté reinterpret_cast, mais c'est un langage différent, donc sa valeur pour vous peut varier:

ISO / IEC N337: Projet de spécification C ++ 11 (je n'ai que le projet en main)

5.2.10 Réinterpréter le casting

  1. Un pointeur peut être explicitement converti en n'importe quel type intégral suffisamment grand pour le contenir. La fonction de mappage est définie par l'implémentation. [Remarque: Il est destiné à ne pas surprendre ceux qui connaissent la structure d'adressage de la machine sous-jacente. —Fin note] Une valeur de type std :: nullptr_t peut être convertie en type intégral; la conversion a la même signification et la même validité qu'une conversion de (void *) 0 en type intégral. [Remarque: Un reinterpret_cast ne peut pas être utilisé pour convertir une valeur de n'importe quel type en type std :: nullptr_t. —Fin note]

  2. Une valeur de type intégral ou de type énumération peut être explicitement convertie en pointeur. Un pointeur converti en un entier de taille suffisante (s'il en existe sur l'implémentation) et renvoyé au même type de pointeur aura sa valeur d'origine; les mappages entre pointeurs et entiers sont autrement définis par l'implémentation. [Remarque: à l'exception de ce qui est décrit au 3.7.4.3, le résultat d'une telle conversion ne sera pas une valeur de pointeur dérivée en toute sécurité. —Fin note]

Comme vous pouvez le voir ici, avec quelques années de plus à son actif, C ++ a constaté qu'il était sûr de supposer qu'un mappage vers des nombres entiers existait, donc il n'est plus question de comportement indéfini (bien qu'il y ait une contradiction intéressante entre les parties 4 et 5 avec la phrase "s'il en existe sur la mise en œuvre")


Maintenant, que devez-vous retirer de cela?

  • La représentation exacte des pointeurs est définie par l'implémentation. (en fait, juste pour le rendre plus désordonné, certains petits ordinateurs intégrés représentent le pointeur nul, (void ) 0, comme adresse 255 pour prendre en charge certaines astuces d'aliasing d'adresse qu'ils utilisent) *
  • Si vous devez vous interroger sur la représentation des pointeurs dans la mémoire, vous n'êtes probablement pas à un moment de votre carrière de programmeur où vous voulez vous amuser avec eux.

Le meilleur pari: lancer un (char *). Les spécifications C et C ++ sont pleines de règles spécifiant le conditionnement des tableaux et des structures, et les deux autorisent toujours le transtypage de n'importe quel pointeur en char *. char est toujours de 1 octet (non garanti en C, mais en C ++ 11, il est devenu une partie obligatoire du langage, donc il est relativement sûr de supposer qu'il est de 1 octet partout). Cela vous permet de faire une certaine arithmétique des pointeurs au niveau octet par octet sans avoir besoin de connaître réellement les représentations spécifiques à l'implémentation des pointeurs.

Cort Ammon - Rétablir Monica
la source
Pouvez-vous nécessairement convertir un pointeur de fonction en un char *? Je pense à une machine hypothétique avec des espaces d'adressage séparés pour le code et les données.
Philip Kendall
@PhilipKendall Bon point. Je n'ai pas inclus cette partie de la spécification, mais les pointeurs de fonction sont traités comme une chose complètement différente des pointeurs de données dans la spécification en raison exactement du problème que vous soulevez. Les pointeurs membres sont également traités différemment (mais ils agissent également très différemment également)
Cort Ammon - Rétablir Monica
A charest toujours 1 octet en C. Citant la norme C: "L'opérateur sizeof donne la taille (en octets) de son opérande" et "Lorsque sizeof est appliqué à un opérande de type char, char non signé ou char signé, (ou une version qualifiée de celui-ci) le résultat est 1. " Vous pensez peut-être qu'un octet a une longueur de 8 bits. Ce n'est pas nécessairement le cas. Pour être conforme à la norme, un octet doit contenir au moins 8 bits.
David Hammen
La spécification décrit la conversion entre les types pointeur et entier. Il faut toujours garder à l'esprit qu'une "conversion" entre types n'implique pas une égalité des types, ni même qu'une représentation binaire des deux types en mémoire aurait le même motif binaire. (ASCII peut être "converti" en EBCDIC. Big-endian peut être "converti" en little-endian. Etc.)
user2338816
1

Sur la plupart des architectures, le type de pointeur cesse d'exister une fois qu'il a été traduit en code machine (à l'exception peut-être des "gros pointeurs"). Par conséquent, un pointeur vers un intne pourrait pas être distingué d'un pointeur vers un double, au moins en soi. *

[*] Bien que vous puissiez toujours faire des suppositions en fonction des types d'opérations que vous lui appliquez.

Rufflewind
la source
1

Une chose importante à comprendre à propos de C et C ++ est ce que sont réellement les types. Tout ce qu'ils font, c'est d'indiquer au compilateur comment interpréter un ensemble de bits / octets. Commençons par le code suivant:

int var = -1337;

Selon l'architecture, un entier reçoit généralement 32 bits d'espace pour stocker cette valeur. Cela signifie que l'espace en mémoire où var est stocké ressemblera à quelque chose comme "11111111 11111111 11111010 11000111" ou en hexadécimal "0xFFFFFAC7". C'est ça. C'est tout ce qui est stocké à cet endroit. Tous les types font dire au compilateur comment interpréter ces informations. Les pointeurs ne sont pas différents. Si je fais quelque chose comme ça:

int* var_ptr = &var;   //the ampersand is telling C "get the address where var's value is located"

Ensuite, le compilateur obtiendra l'emplacement de var, puis stockera cette adresse de la même manière que le premier extrait de code enregistre la valeur -1337. Il n'y a aucune différence dans la façon dont ils sont stockés, juste dans la façon dont ils sont utilisés. Peu importe que j'ai fait de var_ptr un pointeur vers un int. Si vous le vouliez, vous pourriez le faire.

unsigned int var2 = *(unsigned int*)var_ptr;

Cela copiera la valeur hexadécimale ci-dessus de var (0xFFFFFAC7) dans l'emplacement qui stocke la valeur de var2. Si nous devions ensuite utiliser var2, nous trouverions que la valeur serait 4294965959. Les octets dans var2 sont les mêmes que var, mais la valeur numérique diffère. Le compilateur les a interprétés différemment parce que nous lui avons dit que ces bits représentent un long non signé. Vous pouvez également faire de même pour la valeur du pointeur.

unsigned int var3 = (unsigned int)var_ptr;

Vous finiriez par interpréter la valeur qui représente l'adresse de var comme un entier non signé dans cet exemple.

J'espère que cela clarifie les choses pour vous et vous donne un meilleur aperçu du fonctionnement de C. Veuillez noter que vous NE DEVEZ PAS faire l'une des choses folles que j'ai faites dans les deux lignes ci-dessous dans le code de production réel. C'était juste pour la démonstration.

NUL
la source
1

Entier.

L'espace d'adressage dans un ordinateur est numéroté séquentiellement, en commençant à 0, et incrémente de 1. Un pointeur contiendra donc un nombre entier qui correspond à une adresse dans l'espace d'adressage.

galois
la source
1

Les types se combinent.

En particulier, certains types se combinent, presque comme s'ils étaient paramétrés avec des espaces réservés. Les types de tableau et de pointeur sont comme ceci; ils ont un tel espace réservé, qui est le type de l'élément du tableau ou la chose pointée, respectivement. Les types de fonctions sont également comme ceci; ils peuvent avoir plusieurs espaces réservés pour les paramètres et un espace réservé pour le type de retour.

Une variable qui est déclarée contenir un pointeur sur char a le type "pointeur sur char". Une variable déclarée pour contenir un pointeur vers un pointeur vers int a le type "pointeur vers un pointeur vers int".

Une (valeur de) type "pointeur vers pointeur vers int" peut être remplacée par "pointeur vers int" par une opération de déréférencement. Ainsi, la notion de type n'est pas seulement des mots mais une construction mathématiquement significative, dictant ce que nous pouvons faire avec des valeurs du type (telles que la déréférence, ou passer comme paramètre ou assigner à une variable; elle détermine également la taille (nombre d'octets) de opérations d'indexation, d'arithmétique et d'incrémentation / décrémentation).

PS Si vous voulez vous familiariser avec les types, essayez ce blog: http://www.goodmath.org/blog/2015/05/13/expressions-and-arity-part-1/

Erik Eidt
la source