2010-01-24 5 views
3
#include <stdio.h> 

int main(void){ 
    unsigned a[3][4] = { 
    {2,23,6,7}, 
    {8,5,1,4}, 
    {12,15,3,9} 
}; 
printf("%u",*((int*)(((char*)a)+4))); 
return 0; 
} 

La sortie dans ma machine est la valeur à a[0][1] i.e. .Could quelqu'un expliquer comment est-ce travail?Comment fonctionne cet arithmétique de pointeur?

Edit: Rétrogradation à l'ancien code yucky, exactement ce qui m'a été présenté à: P

+0

Oui, c'était une faute de frappe: | –

+0

J'ai édité l'extrait après avoir lu le message de GMan :) –

+0

Merci, mais je ne ferais pas cela. :) Il confond les gens et invalide les réponses que nous avons données. Gardez la question comment elle était afin que tout continue à avoir du sens, et ainsi les passants peuvent tirer pleinement parti de la question. EDIT: Beaucoup mieux. :) – GManNickG

Répondre

13

Vous avez votre tableau en mémoire comme si:

2, 23, 6, 7, 8... 

Qu'est-ce que cela n'est transtyper le tableau un char*, qui vous permet d'accéder à des octets individuels, et points ici:

2, 23, 6, 7, 8... 
^ 

il ajoute quatre octets, en le déplaçant vers la valeur suivante (voir plus plus tard).

2, 23, 6, 7, 8... 
^

Ensuite, il se transforme en un int* et déréférence il, obtenir la valeur 23.


Il y a trois choses techniquement mal avec ce code.

La première est qu'il suppose qu'un unsigned a une taille de 4 octets. (D'où le + 4). Mais ce n'est pas nécessairement vrai! Mieux aurait été + sizeof(unsigned), assurant l'exactitude, peu importe quelle taille unsigned se trouve être.

Le deuxième problème est le cast à int: le tableau original était unsigned, mais la valeur est en train d'être convertie en int. Il existe des valeurs dans la plage unsigned que int ne peut pas représenter (car dans un int la moitié de la plage est dans les négatifs.) Donc, si l'une des valeurs dans le tableau n'était pas représentable comme int (signifiant que la valeur était supérieure à INT_MAX) , vous obtiendriez la mauvaise valeur. Mieux serait de convertir en unsigned*, pour maintenir le bon type.

La dernière chose est le spécificateur de format. Le spécificateur pour les entiers est %d, mais le code utilise %u, qui est pour les entiers non signés. En effet, même si le renvoi à int* était erroné, printf va convertir cette valeur en unsigned*, en restaurant son intégrité. En corrigeant le problème deux, le problème trois se corrige lui-même.

Il ya un caché quatrième problème: Le code est nul. Cela peut être à des fins d'apprentissage, mais beurk.

+0

J'ai compris! Merci :) –

+0

@nthgreek: Pointer arithmétique: http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/pointer.html –

+0

@GMan: 'Il y a un quatrième problème caché: Le code est nul . Cela peut être à des fins d'apprentissage, mais beurk.'- Convenu: P –

2

Il convertit d'abord implicitement le tableau a en un pointeur vers son début. Puis il jette le pointeur sur char * et incrémente la valeur de 4. La valeur 4 est la même que sizeof (unsigned) sur votre système, donc en réalité il a déplacé un élément en avant du début. Puis il jette l'adresse à int * et lit la valeur pointée par lui (opérateur *). Cette valeur résultante est imprimée sous forme d'entier non signé, ce qui fonctionne car int et unsigned ont la même taille.

La disposition de la matrice 2D statique dans la mémoire est telle que tous les éléments sont réellement stockés en séquence en tant que matrice unidimensionnelle.

1

unsigned int est de taille 4. dire sizeof (non signé) == 4

il peut contenir 4 caractères, dont chacun est un octet [en C pas en Java/C#, etc.].

Le tableau est alloué consécutivement dans la mémoire. Lorsque vous traitez un tableau non signé comme char *, vous devez déplacer le curseur de 4 étapes pour atteindre la prochaine valeur non signée dans le tableau.

+0

'sizeof (unsigned) == 4' * dans ce cas *, pas nécessairement partout. C'est peut-être ce que vous vouliez dire. – GManNickG

1

D'abord, vous créez un tableau à 2 dims avec la taille 3x4.

Après ((char*)a) vous pouvez travailler avec ceci comme tableau de caractères. Désignons le comme b.

((char*)a)+4 est la même que b[4], il pointe vers l'élément 5 -th de tableau de char (vous vous souvenez, que aarays en C sont basés sur 0). Ou juste 5 octets.

Lorsque vous convertissez le tableau en int, l'élément i-th de tableau int commence à i*4 octet si sizeof(int) = 4. Ainsi, sur le 5ème octet, le second élément de int array commence là où pointe votre pointeur. Le compilateur obtient 4 octets à partir de la 4ème position et dit que c'est int. C'est exactement un [0] [1].

9

Le tableau:

unsigned a[3][4] = { 
    {2,23,6,7}, 
    {8,5,1,4}, 
    {12,15,3,9} 
}; 

est aménagé dans la mémoire en tant que (en supposant que a est lui-même à l'emplacement de mémoire 0x8000, un endian-ness particulier et pour une période de quatre octets int):

0x8000 0 0 0 2 
0x8004 0 0 0 23 
0x8008 0 0 0 6 
0x800C 0 0 0 7 
0x8010 0 0 0 8 
0x8014 0 0 0 5 
0x8018 0 0 0 14 
0x801C 0 0 0 12 
0x8020 0 0 0 15 
0x8024 0 0 0 3 
0x8028 0 0 0 9 

Briser l'expression:

*((int*)(((char*)a)+4)) 
  • ((char*)a) vous donne un pointeur char.
  • +4 avances que pointeur de 4 octets (4 * sizeof(char))
  • (int*) tour à tour le résultat de ce retour en un pointeur int. Déréférences pointeur pour extraire le int.

C'est une façon très stupide de ce fait, car il est intrinsèquement non-portable (aux environnements où un int est de deux ou huit octets, par exemple).

+1

+1 Juste parce que votre disposition de mémoire de tableau semble impressionnante-sauce comparée à la mienne. – GManNickG

+0

Belle explication +1! :) –

+0

@ GMAN: oui en effet 'la disposition de la mémoire tableau de paxdiablo semble génial» mais j'ai obtenu la solution après avoir lu cette ligne seulement 'Qu'est-ce que cela fait est de lancer le tableau à un char *, qui vous permet d'accéder à des octets individuels :) :) –

Questions connexes