J'ai un pointeur int
et un pointeur char
.
Je sais que ce pointeur stocke simplement une adresse mémoire. Maintenant, si j'ai un pointeur int
mon adresse est incrémentée de 4 octets lorsque je demande la valeur suivante.
Maintenant, où dans le compilateur ou la mémoire stockons-nous les instructions selon lesquelles un pointeur int
doit être incrémenté de 4 octets et un pointeur char
doit être incrémenté de 1 octet lorsque nous demandons une valeur suivante?
3 Réponses :
L'opération effectuée est déterminée par les types de ses opérandes.
Considérons une opération «plus» avec deux opérandes, p + n
. Si le type de p
est «pointeur vers int
» et le type de n
est int
, le compilateur génère des instructions pour calculer l'adresse qui est n
fois sizeof(int)
octets au-delà de où p
pointe. Si le type de p
est «pointeur vers char
» et le type de n
est int
, le compilateur génère des instructions pour calculer l'adresse qui est n
fois sizeof(char)
octets au-delà de où p
pointe.
De même, dans l'expression *p
, si le type de p
est «pointer to int
», le compilateur génère une instruction pour charger un entier de quatre octets à partir de la mémoire. Si le type de p
est «pointeur vers char
», le compilateur génère une instruction pour charger un entier d'un octet à partir de la mémoire.
Pour les opérations en indice, p[n]
est défini comme étant * ((p) + (n)) `, c'est donc une combinaison des deux opérations ci-dessus.
(Notez que ce qui précède s'applique à la simple compilation d'expressions isolées. Dans l'ensemble, les compilateurs peuvent optimiser les programmes, donc l'assembly résultant peut être différent de ce qui est décrit ci-dessus, mais il aura les mêmes effets.)
L'incrément est stocké dans le code d'assemblage. Vous pouvez voir quelque chose qui est soit eax + 4 ou 3 [eax] (btw ces deux ne sont pas les mêmes). Essentiellement, le code d'assemblage indique à l'ordinateur pendant l'exécution comment incrémenter.
Comment le compilateur sait-il faire cela? Eh bien, cela vient du code qui fait le compilateur. Il crée ce qu'on appelle une arborescence système abstraite ou plus utilement un graphe. Ces graphiques peuvent garantir que les règles sont suivies, comme l'incrément.
Si cela vous intéresse, lisez un livre sur les compilateurs et l'architecture informatique.
Essayez-le.
c: e5b31004 ldr r1, [r3, #4]! 30: e5f20001 ldrb r0, [r2, #1]! 58: e1f210b2 ldrh r1, [r2, #2]!
Et vous voyez clairement:
int fun1 ( int *x ) { unsigned int ra; int sa; sa=0; for(ra=0;ra<100;ra++) { sa+=x[ra]; } return(sa); } char fun2 ( char *x ) { unsigned int ra; char sa; sa=0; for(ra=0;ra<100;ra++) { sa+=x[ra]; } return(sa); } char fun3 ( short *x ) { unsigned int ra; short sa; sa=0; for(ra=0;ra<100;ra++) { sa+=x[ra]; } return(sa); } 00000000 <fun1>: 0: e3a02000 mov r2, #0 4: e2403004 sub r3, r0, #4 8: e2800f63 add r0, r0, #396 ; 0x18c c: e5b31004 ldr r1, [r3, #4]! 10: e1530000 cmp r3, r0 14: e0822001 add r2, r2, r1 18: 1afffffb bne c <fun1+0xc> 1c: e1a00002 mov r0, r2 20: e12fff1e bx lr 00000024 <fun2>: 24: e3a03000 mov r3, #0 28: e2402001 sub r2, r0, #1 2c: e2801063 add r1, r0, #99 ; 0x63 30: e5f20001 ldrb r0, [r2, #1]! 34: e0833000 add r3, r3, r0 38: e1520001 cmp r2, r1 3c: e20330ff and r3, r3, #255 ; 0xff 40: 1afffffa bne 30 <fun2+0xc> 44: e1a00003 mov r0, r3 48: e12fff1e bx lr 0000004c <fun3>: 4c: e3a03000 mov r3, #0 50: e2402002 sub r2, r0, #2 54: e280c0c6 add r12, r0, #198 ; 0xc6 58: e1f210b2 ldrh r1, [r2, #2]! 5c: e0813003 add r3, r1, r3 60: e1a00803 lsl r0, r3, #16 64: e1a03000 mov r3, r0 68: e152000c cmp r2, r12 6c: e1a00820 lsr r0, r0, #16 70: e1a03843 asr r3, r3, #16 74: 1afffff7 bne 58 <fun3+0xc> 78: e20000ff and r0, r0, #255 ; 0xff 7c: e12fff1e bx lr
Vous pouvez également voir la gestion des opérations de demi-mot et d'octet (cette architecture utilise des registres 32 bits).
Le compilateur se souvient du type du pointeur et génère les instructions machine appropriées. Il utilise des structures internes pour enregistrer le type, l'identifiant et si une variable a été initialisée ou utilisée, afin de pouvoir émettre des avertissements. Ceux-ci ne font pas partie du code généré.
int et char, etc. sont une notion du langage de haut niveau. Que s'est-il passé lorsque vous l'avez essayé?
Quand je vais couper un morceau de bois de deux pieds de long, je me souviens que je veux qu'il fasse deux pieds de long et le marque et le coupe comme tel. Ni la scie ni le bois n'en sont conscients, cette information n'existe que dans mon esprit.