0
votes

Comment le compilateur différencie-t-il les pointeurs int et char?

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 commentaires

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.


3 Réponses :


1
votes

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.)


0 commentaires

0
votes

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.


0 commentaires

0
votes

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).


0 commentaires