c++ - pour - pointeur tableau




Dans quelle mesure est-il acceptable de considérer les pointeurs C++ comme des adresses mémoire? (8)

À moins que les pointeurs ne soient optimisés par le compilateur, ce sont des entiers qui stockent des adresses mémoire. Leur longueur dépend de la machine pour laquelle le code est compilé, mais ils peuvent généralement être traités comme des ints.

En fait, vous pouvez vérifier cela en imprimant le numéro réel stocké sur eux avec printf() .

Attention cependant, les opérations d'incrément / décrément de type * pointeur sont effectuées par sizeof(type) . Voyez par vous-même avec ce code (testé en ligne sur Repl.it):

#include <stdio.h>

int main() {
    volatile int i1 = 1337;
    volatile int i2 = 31337;
    volatile double d1 = 1.337;
    volatile double d2 = 31.337;
    volatile int* pi = &i1;
    volatile double* pd = &d1;
    printf("ints: %d, %d\ndoubles: %f, %f\n", i1, i2, d1, d2);
    printf("0x%X = %d\n", pi, *pi);
    printf("0x%X = %d\n", pi-1, *(pi-1));
    printf("Difference: %d\n",(long)(pi)-(long)(pi-1));
    printf("0x%X = %f\n", pd, *pd);
    printf("0x%X = %f\n", pd-1, *(pd-1));
    printf("Difference: %d\n",(long)(pd)-(long)(pd-1));
}

Toutes les variables et les pointeurs ont été déclarés volatils de sorte que le compilateur ne les optimiserait pas. Notez également que j'ai utilisé la décrémentation, car les variables sont placées dans la pile de fonctions.

La sortie était:

ints: 1337, 31337
doubles: 1.337000, 31.337000
0xFAFF465C = 1337
0xFAFF4658 = 31337
Difference: 4
0xFAFF4650 = 1.337000
0xFAFF4648 = 31.337000
Difference: 8

Notez que ce code peut ne pas fonctionner sur tous les compilateurs, spécialement s'ils ne stockent pas les variables dans le même ordre. Cependant, ce qui est important est que les valeurs de pointeur peuvent réellement être lues et imprimées et que les décréments de l'un peuvent / seront décrémentés en fonction de la taille de la variable référencée par le pointeur.

Notez également que & et * sont des opérateurs réels pour référence ("obtenir l'adresse mémoire de cette variable") et dereference ("obtenir le contenu de cette adresse mémoire").

Cela peut également être utilisé pour des astuces sympatiques comme l'obtention des valeurs binaires IEEE 754 pour les flottants, en lançant float* comme int* :

#include <iostream>

int main() {
    float f = -9.5;
    int* p = (int*)&f;

    std::cout << "Binary contents:\n";
    int i = sizeof(f)*8;
    while(i) {
        i--;
        std::cout << ((*p & (1 << i))?1:0);
   } 
}

Le résultat est:

Binary contents:
11000001000110000000000000000000 

Exemple tiré de https://pt.wikipedia.org/wiki/IEEE_754 . Découvrez sur n'importe quel convertisseur.

Lorsque vous apprenez C ++, ou du moins lorsque je l'ai appris via C ++ Primer , les pointeurs ont été appelés les "adresses mémoire" des éléments vers lesquels ils pointent. Je me demande dans quelle mesure cela est vrai.

Par exemple, est-ce que deux éléments *p1 et *p2 ont la propriété p2 = p1 + 1 ou p1 = p2 + 1 si et seulement s'ils sont adjacents dans la mémoire physique?


Comme d'autres variables, le pointeur stocke une donnée qui peut être une adresse de mémoire où d'autres données sont stockées.

Donc, pointeur est une variable qui a une adresse et peut contenir une adresse.

Notez qu'il n'est pas nécessaire qu'un pointeur conserve toujours une adresse . Il peut contenir un ID / handle de non-adresse etc. Par conséquent, dire pointeur comme une adresse n'est pas une chose sage.

En ce qui concerne votre deuxième question:

L'arithmétique de pointeur est valide pour un morceau de mémoire contigu. Si p2 = p1 + 1 et les deux pointeurs sont du même type, alors p1 et p2 pointent vers un morceau de mémoire contigu. Ainsi, les adresses p1 et p2 sont adjacentes les unes aux autres.


D'une manière ou d'une autre, les réponses ne mentionnent pas une famille spécifique de pointeurs, c'est-à-dire des pointeurs vers des membres. Ce ne sont certainement pas des adresses de mémoire.


Je pense que share a la bonne idée mais une mauvaise terminologie. Ce que les pointeurs C fournissent sont exactement le contraire de l'abstraction.

Une abstraction fournit un modèle mental relativement facile à comprendre et à raisonner, même si le matériel est plus complexe et difficile à comprendre ou plus difficile à raisonner.

Les pointeurs C sont l'opposé de cela. Ils prennent en compte les éventuelles difficultés du matériel, même si le matériel réel est souvent plus simple et plus facile à raisonner. Ils limitent votre raisonnement à ce qui est permis par une union des parties les plus complexes du matériel le plus complexe, quelle que soit la simplicité du matériel.

Les pointeurs C ++ ajoutent une chose que C n'inclut pas. Cela permet de comparer tous les pointeurs du même type pour l'ordre, même s'ils ne sont pas dans le même tableau. Cela permet un peu plus d'un modèle mental, même s'il ne correspond pas parfaitement au matériel.


Le système d'exploitation fournit une abstraction de la machine physique à votre programme (c'est-à-dire que votre programme s'exécute dans une machine virtuelle). Ainsi, votre programme n'a accès à aucune ressource physique de votre ordinateur, qu'il s'agisse du temps processeur, de la mémoire, etc. il suffit de demander l'OS pour ces ressources.

Dans le cas de la mémoire, votre programme fonctionne dans un espace d'adressage virtuel, défini par le système d'exploitation. Cet espace adresse a plusieurs régions, telles que la pile, le tas, le code, etc. La valeur de vos pointeurs représente les adresses dans cet espace d'adressage virtuel. En effet, 2 pointeurs vers des adresses consécutives pointeront vers des emplacements consécutifs dans cet espace d'adressage.

Cependant, cet espace d'adressage est découpé par le système d'exploitation en pages et segments, qui sont échangés de la mémoire selon les besoins, de sorte que vos pointeurs peuvent ou non pointer vers des emplacements de mémoire physique consécutifs et est impossible de dire si cela est Vrai ou pas. Cela dépend également de la stratégie utilisée par le système d'exploitation pour la pagination et la segmentation.

Bottom line est que les pointeurs sont des adresses de mémoire. Cependant, ce sont des adresses dans un espace mémoire virtuel et c'est au système d'exploitation de décider comment cela est mappé à l'espace mémoire physique.

En ce qui concerne votre programme, ce n'est pas un problème. Une raison de cette abstraction est de faire croire aux programmes qu'ils sont les seuls utilisateurs de la machine. Imaginez le cauchemar que vous auriez à traverser si vous deviez tenir compte de la mémoire allouée par d'autres processus lorsque vous écrivez votre programme - vous ne savez même pas quels processus vont se dérouler simultanément avec le vôtre. Aussi, c'est une bonne technique pour renforcer la sécurité: votre processus ne peut pas (du moins, ne devrait pas pouvoir) accéder malicieusement à l'espace mémoire d'un autre processus puisqu'ils s'exécutent dans 2 espaces mémoire (virtuels) différents.


Les pointeurs sont des adresses de mémoire, mais vous ne devez pas supposer qu'ils reflètent l'adresse physique. Lorsque vous voyez des adresses comme 0x00ffb500 ce sont des adresses logiques que la MMU traduira vers l'adresse physique correspondante. C'est le scénario le plus probable, puisque la mémoire virtuelle est le système de gestion de la mémoire le plus étendu, mais il pourrait y avoir des systèmes qui gèrent directement l'adresse physique


Tout à fait raison de penser aux pointeurs comme adresses de mémoire. C'est ce qu'ils sont dans TOUS les compilateurs avec lesquels j'ai travaillé - pour un certain nombre d'architectures de processeurs différentes, fabriquées par un certain nombre de producteurs de compilateurs différents.

Cependant, le compilateur fait une magie intéressante, pour vous aider avec le fait que les adresses mémoire normales [dans tous les processeurs traditionnels modernes au moins] sont des adresses octets, et l'objet auquel votre pointeur fait référence peut ne pas être un octet. Donc, si nous avons T* ptr; , ptr++ fera ((char*)ptr) + sizeof(T); ou ptr + n est ((char*)ptr) + n*sizeof(T) . Cela signifie aussi que p1 == p2 + 1 requiert que p1 et p2 soient du même type T , puisque le +1 est réellement +sizeof(T)*1 .

Il y a UNE exception à la ci-dessus "les pointeurs sont des adresses de mémoire", et c'est des pointeurs de fonction de membre. Ils sont "spéciaux", et pour l'instant, s'il vous plaît ignorez simplement comment ils sont réellement mis en œuvre, suffisant pour dire qu'ils ne sont pas "simplement des adresses mémoire".


Vous devriez considérer les pointeurs comme des adresses de mémoire virtuelle : les systèmes d'exploitation grand public modernes et les environnements d'exécution placent au moins une couche d'abstraction entre la mémoire physique et ce que vous voyez comme une valeur de pointeur.

En ce qui concerne votre déclaration finale, vous ne pouvez pas faire cette supposition, même dans un espace d'adressage de mémoire virtuelle. L'arithmétique du pointeur n'est valide que dans les blocs de mémoire contigus tels que les tableaux. Et bien qu'il soit permis (en C et C ++) d'assigner un pointeur à un point après un tableau (ou un scalaire), le comportement de déférencement d' un tel pointeur n'est pas défini. Hypothèse sur la contiguïté dans la mémoire physique dans le contexte de C et C ++ est inutile.





hardware