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.
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
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:
adicionando um elemento neutro de tipo ''float'' à expressão;
forçando a avaliação de ''soma'' ou ''num'' como float.
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.
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 ;
}
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.