binjour,
quelqu'un peut m'expliquer pourquoi
etCode:
1
2
3
4 int x=10,*ptr; ptr=&x; cout<<*(&ptr)<<endl;
renvoie la meme adresseCode:cout<<&(*ptr)<<endl;
Merci èa vous
Version imprimable
binjour,
quelqu'un peut m'expliquer pourquoi
etCode:
1
2
3
4 int x=10,*ptr; ptr=&x; cout<<*(&ptr)<<endl;
renvoie la meme adresseCode:cout<<&(*ptr)<<endl;
Merci èa vous
Salut,
&ptr donne l'adresse du pointeur (généralement, on s'en moque...), et l'opérateur * donne la valeur pointée par cette adresse, et donne donc... ptr !Code:cout<<*(&ptr)<<endl;
*ptr déréférence le pointeur (c'est à dire : donne la valeur pointée), et tu récupères ensuite l'adresse de cette valeur, tu retombes à nouveau sur ptr.Code:cout<<&(*ptr)<<endl;
Dans les deux cas tu affiches la valeur de ptr, qui est l'adresse de x. ptr a lui même une adresse, qui n'a rien à voir avec sa valeur.
Ces quatre expressions donnent toutes la même valeur :
- *(&ptr)
- &(*ptr)
- ptr
- &x
J'ai le même code asm pour les 4 formes. Cependant, j'aurais (peut être à tort) toujours une angoisse à écrire &(*ptr) pour ptr non initialisé alors que pour les autres formes, je n'y verrais pas d'inconvénient.
Écrire &*ptr est bien sûr un comportement indéfini si ptr ne pointe pas vers un objet valide.
Merci beaucoup pour vos reponses.
en effet vu de cette maniere tout est claire. cependant un question me vientà l'espris en lisant les reponses quand tu as dis que * dereference ptr. je comprends ca tres bien
par contre dans la declarationqu'estce qui fais qu'on peux declarer un tableau comme de cette maniere.Code:int ** ptr;*ptr = new int[10]
le premier * est pour definir un tableau de une dimension qui contient des pointer de type int c'est ca
ce que j'essai de comprends c'est l'emploi de * en general s'il ya un article un tuto qui explique ca bien ca sera bievenue!
Merci
Ce code est faux et provoque un comportement indéfini.Code:
1
2
3 int ** ptr; *ptr = new int[10];
Évite de parler de dimensions et de tableaux, car int** ptr n'a rien d'un tableau. ptr est ici un pointeur sur pointeur sur int.Citation:
Envoyé par mahboub
Si tu veux un tableau de pointeurs sur int, tu dois faire comme ceci :
L'instruction "new TYPE" retourne l'adresse d'une zone mémoire de type pointeur sur TYPE, donc new int* retourne bien une adresse de type int**.Code:
1
2 int** ptr = NULL; ptr = new int* [10];
Voyons maintenant pourquoi ton code était faux : *ptr = new int[10];
*ptr déréférence le pointeur, ce qui donne un int*.
Mais qu'en est-il de sa valeur ? Impossible à prédire car le pointeur n'est pas initialisé.
Lorsque tu déclares un pointeur, tu dois toujours lui mettre une valeur (soit l'adresse de l'élément pointé, soit NULL, ça dépend du contexte d'utilisation).
new int[10] retourne l'adresse d'une zone mémoire allouée dynamiquement (une zone de 10 int), et tu écris cette adresse à *ptr. Un pointeur non initialisé contient bien une adresse, c'est à dire ce qu'il y avait avant à cet emplacement mémoire. Et il y a de forte chance que cette adresse n'appartienne pas à ton programme, tu tentes donc d'écrire dans une zone qui ne t'appartient pas (c'est une violation mémoire, l'OS tue ton programme et tu obtiens une erreur de segmentation).
« * » désigne un pointeur. Donc « int * » désigne un pointeur sur un entier, soit une variable qui contient l'adresse en mémoire d'un entier quelquonque. Or, si un pointeur est en soi une variable, il a également une adresse, et il peut donc être référencé.
« int ** » est donc un pointeur sur un pointeur sur un entier ;
« int *** » est un pointeur sur un pointeur sur un pointeur sur un entier;
etc.
Ce qui distingue un pointeur d'un autre type de données, c'est que l'adresse qu'il contient est a priori celle de quelque chose d'autre. Cette adresse peut donc être exploitée en tant que telle (affichage, comparaison, ...) ou bien suivie pour atteindre ce qu'il pointe (indirection). Tant que l'on tombe sur un pointeur, on peut continuer. Tous les autres types sont terminaux.
Maintenant, les tableaux : en C et C++, la mémoire n'est pas particulièrement structurée quand tu déclares un tableau. La principale chose que fait le langage est réserver suffisamment de mémoire contigüe pour y stocker tous tes éléments. Dès lors, la seule chose que tu as besoin de connaître pour exploiter ton tableau est l'endroit où il commence. Après, tu n'as qu'à multiplier les numéro du rang et taille de ton élément pour connaître le décalage (offset) par rapport au début (adresse de base) et le retrouver.
C'est pourquoi, lorsque tu déclares un tableau en C ou C++, il y a systématiquement un pointeur créé avec. Celui-là même qui te donne son emplacement en mémoire. Ainsi un « char[10] » occupera 14 octets (10 + 4). Un « char [] » ne déclarera donc le pointeur et sera donc équivalent à « char * ». D'ailleurs, tu peux utiliser comme tableau n'importe quel pointeur même s'il n'a pas été déclaré à l'avance avec des crochets. Tu peux aussi utiliser l'arithmétique des pointeurs : « (ptr + 1) » ne te donnera pas forcément l'adresse du prochain octet en mémoire, mais l'emplacement du prochain élément (une sorte de « (ptr + 1*sizeof(type) »).
Enfin, comme l'a signalé Haze, ton code avec **ptr est incorrect ici ... parce que ptr n'est pas initialisé. Dans un autre contexte, il aurait été parfaitement valide et aurait représenté ... un tableau de tableaux !
En fait, avec un type simple comme donnée en exemple au début du post, le code généré par Visual et gcc (windows) est le même quelque soit l'écriture et correspond uniquement à récupérer la valeur de prt. Donc, il n'y a pas de déréférencement fait sur prt. L'exemple &*ptr ne plante pas même si ptr n'est pas initialisé. C'est une optimisation du compilo mais j'avoue que je ne m'y fierait pas et que j'éviterais cette forme d'écriture.
En pratique, oui, ça ne fait rien.
D'après le standard, néanmoins, c'est un comportement indéfini de déréferencer un pointeur non initialisé ou nul, qu'on utilise le résultat avec & ou non.
Simplement en lire la valeur, sans la déréférencer, est déjà non défini par le standard, et donc, en mode chipotage, les écritures *(&ptr) et ptr ont aussi un problème dans ce cas.
C'est un probleme si ptr n'est pas initialise ou est invalide. Par exemple apres
C'est du comportement indefini. (Il y a des architectures ou charger un pointeur invalide dans un registre peut generer une interruption. Les x86 en mode protege par exemple si on utilise les segments.)Code:
1
2 ptr = new ?; delete ptr;
Ben oui ça marche: Ce n'est pas une lecture, c'est une écriture.
J'avais bêtement toujours cru qu'un pointeur ca restait un emplacement mémoire comme un autre qui prenait son sens qu'au moment du déréférencement: là effectivement, on fait un saut vers un autre emplacement mémorie, celui dont l'adresse est contenue dans le pointeur.
Mais, là, dans ce genre d'architecture, cela signifie que les pointeurs sont dans des espaces mémoires différents que les autres variables? Sinon, comment sait-on si on charge un pointeur dans un registre ou un entier par exemple?
Le problème, c'est que le seul fait de lire une valeur non-initialisée est toujours un comportement indéfini, quel que soit le type de la variable. Car selon la plate-forme, il reste toujours la possibilité de tomber sur une "trap value", une représentation qu'il est illégal d'avoir dans un registre.
http://blogs.msdn.com/oldnewthing/ar...2/8679191.aspx
Ok, je comprends. Merci pour ces infos. Je regarde ton lien.