2013-04-27 1 views
-4

Version 1:déréférencement d'un pointeur double niveau des résultats dans le comportement différent de déréférencement d'un pointeur à un seul niveau

int* work(int** pointer, int offset) 
{ 
    return *pointer + (offset/sizeof(int)); 
} 

int main() 
{ 
    int** pointer = (int**) 0x4df73c; 
    int offset = 0xf4; 

    int* healthLowestPointer = work(pointer, offset); 

    while(true) { 
     *healthLowestPointer = 1000; 
     Sleep(250); 
    } 
} 

Version 2:

int* work(int* pointer, int offset) 
{ 
    return (int*) (*pointer + (offset/sizeof(int))); 
} 

int main() 
{  
    int* pointer = (int*) 0x4df73c; 
    int offset = 0xf4; 

    int* healthLowestPointer = work(pointer, offset); 

    while(true) { 
     *healthLowestPointer = 1000; 
     Sleep(250); 
    } 
} 

Version 1 fonctionne correctement, mais la version 2 n » ne semble pas. Je ne comprends pas pourquoi la version 2 est cassée. Est-ce que déréférencer un pointeur à deux niveaux n'est pas la même chose que de déréférencer un pointeur à un niveau, c'est-à-dire qu'il saisit la valeur à l'adresse mémoire que le pointeur contient?

Comment est-ce que j'écrirais une fonction qui prend un pointeur de niveau n en entrée, et renvoie un pointeur de niveau 1 en déréférençant le pointeur n-1 fois?

+0

Les deux versions sont UB. –

+1

Bonne nuit SO, pas plus de ces questions aujourd'hui .../OFF –

+0

@Armin: Je ne suis pas sûr de ce que vous voulez dire. La version 1 est cohérente et fonctionne toujours. – user2327287

Répondre

2

Ce sont des choses très différentes. Un int** est un pointeur vers un pointeur vers un int. Si vous le dérérez, vous obtenez un int*. Un int* est un pointeur vers un int. Si vous le dérérez, vous obtenez un int.

Dans le premier exemple, vous transmettez un int** avec la valeur 0x4df73c comme premier argument à work. Vous déréférencer ceci, ce qui devrait vous donner un int*. C'est, 0x4df73c est l'adresse d'une adresse et faire *pointer a obtenu la deuxième adresse. Vous faites alors l'arithmétique de pointeur avec ce int* en ajoutant (offset/sizeof(int)), qui établit combien de int il y a avec offset bytes. Ainsi, lorsque vous ajoutez cette valeur, votre int* va se déplacer pour pointer sur int à ce décalage. Vous retournez alors ce int* et tout va bien. Dans le deuxième exemple, vous transmettez 0x4df73c comme int*. Vous le dérérez ensuite, ce qui vous donne un int. Maintenant, le + ne fait pas d'arithmétique de pointeur - il fait l'arithmétique entière. (offset/sizeof(int)) vous donnera toujours le nombre de int s dans offset octets, mais l'arithmétique ne fera pas ce que vous voulez. Il n'augmentera pas la valeur du int de la quantité appropriée.

Supposons qu'un int soit 4 octets sur votre ordinateur. Lorsque vous avez un int* appelé p et que vous faites p + 5, ceci n'ajoute pas seulement 5 à l'adresse p. Il ajoute 5 fois la taille d'un int (20 octets), de sorte qu'il pointe maintenant au 5e int. Cependant, si vous avez un int appelé i et que vous faites i + 5, cela ne fait qu'ajouter 5.

Voilà votre problème. Lorsque vous ajoutez à int, vous ne faites pas l'arithmétique appropriée. J'imagine que ce serait travail si vous changez à, en supposant un int* et un int de même taille sur votre système (comme vous le dites qu'ils sont):

return (int*) (*pointer + offset); 

mais je ne recommande pas cet. N'utilisez pas un int comme s'il s'agissait d'un pointeur.Le casting au (int*) implique un reinterpret_cast et est horrible. Stick à votre premier code.

+0

Existe-t-il une raison pour laquelle reinterpret_cast est mauvais en dehors du fait que la seconde version est plus confuse/cryptique pour quelqu'un qui essaie de lire le code? – user2327287

+0

@ user2327287 C'est mauvais parce que le standard laisse une grande partie de son comportement indéfini. Pratiquement la seule chose que vous pouvez faire avec 'reinterpret_cast' est de transtyper de' T *' vers un autre 'U *', puis de revenir à' T *' et de garantir qu'il fonctionne toujours. Tout le reste quitte le royaume de la raison. Vous supposez que l'interprétation d'un int comme int est une bonne chose, ce qui n'est généralement pas le cas. –

+0

Est-il également mauvais d'interpeller 'int **' que 'int *'? – user2327287

0

L'arithmétique de pointeur et l'arithmétique d'entier ne sont pas la même chose.

Ignorant la question de savoir si l'écriture aux emplacements que vous utilisez est valide, tenez compte du résultat de ces derniers:

int i=5; 
int j=i+1; // now j==6 
int* pi = &i; // let's say pi is 0x1000 
int* pj = &i+1 // now pj is 0x1000 + (sizeof int) 
Questions connexes