Cuidado com conversões de precisões integrais diferentes…

Um dos problemas que o desavisado programador pode enfrentar ao usar os tipos integrais (int, short, long int, char, long long int) é o causado pela conversão “automática” de C e C++. O problema não é proeminente com os tipos unsigned, onde se pressupõe que os bits excedentes serão preenchidos com zeros (no caso da conversão de um tipo com menos bits para um com mais bits, shortint, por exemplo); ou o resultado final terá os bits superiores ceifados (no caso de intshort, por exemplo). O problema acontece com os tipos sinalizados. Vejamos um exemplo:

int x = -32769;
short y = x;

printf("x=%d, y=%hd\n", x, y);

Se fizermos um programinha com o fragmento de código acima, obteremos -32769 e 32767 como resposta! É surpreendente que um valor negativo tenha se tornado um valor positivo no sentido oposto da faixa dos inteiros? Veja o que o compilador faz:

...
  mov   eax,[x]
  movsx edx,ax     ; copia os 16 bits inferiores
                   ; estendendo o sinal (short y = x).
  mov   esi,eax
  lea   edi,[fmt]

  ; printf espera EDI=fmt, ESI=x, EDX=y.
  call  printf

Acontece que o valor de x é, em hexadecimal, 0xffff7fff. Neste caso AX conterá 0x7fff que tem o bit 15 zerado e, portanto, na aritmética inteira com sinal, é considerado como um valor positivo. Assim a função MOVSX simplesmente preencherá EDX com zeros à partir do bit 16. Ou seja, EDX será 0x00007fff, que é exatamente 32767, positivo.

Se mudássemos x para um valor dentro da faixa do tipo short, como -32768 (0xffff8000), o bit 15 estará setado e a conversão feita por MOVSX será correta.

Isso não funciona apenas para valores negativos do tipo com mais bits… considere o valor de x como sendo 98304 (0x00018000). Ao convertê-lo para short obteremos -32768 (0x8000 ou, em 32 bits com o sinal estendido, 0xffff8000) pelos mesmos motivos citados acima.

Na conversão de tipos com menos precisão para os de mais precisão (shortint, por exemplo) isso não é problemático, já que MOVSX vai funcionar direitinho. A dica é, então: Se você precisar fazer conversão para uma precisão integral menor, tenha certeza que o valor do tipo “maior” esteja dentro da faixa representável pelo tipo “menor”, senão o resultado pode ser “imprevisível”.

Anúncios