Conversão de tipos

Muitas vezes, uma expressão lógica ou aritmética envolve variáveis e constantes de tipos distintos, como ''char'', ''int'', ''float'' e ''double''. Algumas conversões são efetuadas implicitamente pelo compilador, sem riscos para a integridade dos dados manipulados. Todavia, algumas conversões podem levar à perda de informação e precisam ser "forçadas" pelo programador, através de conversões explícitas.

Conversão implícita

Quando uma expressão envolve operandos de diversos tipos, o compilador primeiro os converte para um único tipo, antes de avaliar a expressão. Nos caso de tipos numéricos, essa conversão é definida pela regra de promoção automática, segundo a qual os tipos de tamanho menor (com menos bytes) são automaticamente convertidos ("promovidos") para tipos de maior tamanho (com mais bytes).

A hierarquia de tipos considerada para a promoção automática é a seguinte (os tipos ''unsigned'' estão no mesmo nível de hierarquia que seus equivalentes ''signed''.):

  char < short < int < long < long long < float < double < long double

Por exemplo, na expressão a seguir, as variáveis à direita da atribuição (sinal '=') são convertidas para um tipo único, antes da expressão ser avaliada:

char   c = 'A' ;
int    i = 3, j = 54;
short  k = 31 ;
float  x = 31.8 ;
double y ;

j = c + 30 ;    //  o valor de c (65) é convertido para int

y = i * x + k ; // os valores de i e k são convertidos para float

A conversão implícita (ou automática) é realizada para as expressões aritméticas (+ - * / %), relacionais (< <= > >= == !=), com bits (& | ^) e para o operador condicional (? :).

Os operadores de atribuição (=, =+, ...) também sofrem uma conversão implícita de tipo, para compatibilizar o valor atribuído (lado direito) à variável que está sendo atribuída (lado esquerdo). Em alguns casos, a conversão implícita na atribuição pode levar à perda de informação. Vejamos alguns exemplos.

char c = 'A' ;
int i, j = 34 ;
long long k = 12345677890 ;
float x, y = 451.28 ;
double pi = 3.141592653589793264 ;

i = c ;     // ok, pois int > char
x = i ;     // ok, pois float > int
i = y ;     // problema, parte fracionária é truncada
x = pi ;    // problema, perda de precisão
i = k ;     // problema, perde os bits mais significativos

Conversão explícita

Em alguns casos, é necessário forçar a conversão de tipos de dados, para que uma expressão seja avaliada da forma desejada pel[ao] programador[ao].

Por exemplo:

int soma, num ;
float media ;

soma = 100 ;
num  = 40 ;

media = soma / num ;        // media = 2.0 (por que?)

No caso acima, a expressão ''soma / num'' será avaliada como ''int / int'', resultando em uma divisão inteira e consequente perda de precisão. Para gerar o resultado correto, a expressão deve ser avaliada como ''float''. Isso pode ser obtido de duas formas:

int soma, num ;
float media ;

soma = 100 ;
num  = 40 ;

media = soma / num ;            // errado, perda de precisão
media = 1.0 * soma / num ;      // soma é "promovida" a float
media = (float) soma / num ;    // soma é avaliada como float (casting)
media = soma / (float) num ;    // num é avaliado como float (casting)

A avaliação forçada de um valor ou variável para um tipo específico usando o prefixo ''(type)'', como no exemplo acima, é chamada type casting. Essa operação é muito usada, sobretudo na avaliação de ponteiros.

Conversão de/para strings

No caso específico de strings, a conversão destas para outros tipos pode ser efetuada através de funções específicas:

#include <stdlib.h>

double atof (const char *str);      // string to float (double), ascii to float

int    atoi (const char *str);      // string to integer, ascii to int

long   atol (const char *str);      // string to long, ascii to long

long long atoll (const char *str);  // string to long long

No outro sentido, a forma mais simples de converter um dado de qualquer tipo para string é usando a função ''sprintf'', que formata e "imprime" o dado em uma string, de forma similar ao que a função ''printf'' realiza na saída padrão:

#include <stdio.h>

int main ()
{
   char buffer[256] ;
   float x ;
  
   x = 32.4 / 7 ;

   sprintf (buffer, "%5.4f", x) ; // "imprime" x na string buffer

   printf ("%s\n", buffer) ;
  
   return 0 ;
}

Conversão de ponteiros

Uma operação frequente em C é a conversão de tipos de ponteiros. Muitas funções importantes, como ''qsort'' e ''bsearch'', usam ponteiros genéricos ''void*'' como parâmetros de entrada, para poder operar sobre dados de diversos tipos.

Como ponteiros para ''void'' não apontam para nenhum tipo válido de dado, eles não podem ser desreferenciados; não é possível acessar diretamente os dados que eles apontam. Por isso, ponteiros para ''void'' precisam ser convertidos em ponteiros para algum tipo válido antes de serem desreferenciados.

O exemplo a seguir mostra como ponteiros ''void'' são convertidos em ''int'', dentro da função ''compara_int(a,b)'':

#include <stdio.h>
#include <stdlib.h>

int vetor[1000] ;
int vsize ;

// compara dois inteiros apontados por "a" e "b"
int compara_int (const void* a, const void* b)
{
   int *pa, *pb ;

   pa = (int*) a ;  // passa a ver a como int*
   pb = (int*) b ;  // idem, b

   if (*pa > *pb) return  1 ;
   if (*pa < *pb) return -1 ;
   return 0 ;
}

int main ()
{
   int i ;

   // preenche o vetor de inteiros com aleatórios
   vsize = 1000 ;
   for (i = 0; i < vsize; i++)
      vetor[i] = random() % 1000 ;

   // ordena o vetor (man qsort)
   qsort (vetor, vsize, sizeof(int), compara_int) ;

   // escreve o vetor
   for (i = 0; i < vsize; i++)
      printf ("%d ", vetor[i]) ;
   printf ("\n") ;
}

Leitura complementar: Type Conversions, C in a Nutshell.