====== Conversão de tipos ====== {{ progc_conversao.mkv |Video desta aula}} {{ transformation.png|https://www.safe.com/what-is/data-transformation/}} Muitas vezes, expressões lógicas/aritméticas envolvem valores de tipos distintos, como ''char'', ''int'', ''float'' e ''double'', que devem ser convertidos durante o cálculo. Algumas conversões são efetuadas implicitamente pelo compilador, sem riscos para a integridade dos dados manipulados. Todavia, certas conversões podem levar a resultados errados ou à perda de informação e precisam ser "forçadas" pelo programador, através de conversões explícitas. ===== Conversão implícita ===== Quando uma operação lógica/aritmética envolve dois operandos de tipos distintos, o compilador primeiro os converte para um único tipo, antes de avaliar a operação. Nos caso de tipos numéricos, essa conversão é denominada **promoção automática**, Na promoção automática, o tipo de menor tamanho (com menos bytes) é automaticamente convertido ("promovido") para o tipo 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 A regra de promoção automática é aplicada separadamente **A CADA OPERAÇÃO** da expressão lógica/aritmética. O código a seguir mostra alguns exemplos de promoção automática: char c = 'A' ; int i = 3 ; short s = 31 ; float f = 31.8 ; double d ; i = c + 30 ; // o valor de c (65) é convertido de char para int d = i * f + s ; // ao avaliar i*x, i é promovido de int a float // ao avaliar float + s, s é promovido de short a float A conversão implícita (ou automática) é realizada para as operações aritméticas (''+ - * / %''), relacionais (''< <= > >= == !='') e com bits (''& | ^''). Os operadores de atribuição (''='', ''+='', ...) também recebem uma conversão implícita de tipo, para compatibilizar o valor calculado (lado direito da atribuição) com a variável que irá recebê-lo (lado esquerdo). ===== Erros de conversão ===== A regra de promoção automática é aplicada a cada operação da expressão lógica/aritmética. Por isso, deve ser tomado cuidado em expressões mais complexas. O código abaixo traz um exemplo dos riscos envolvidos: #include int main () { int i = 100 ; int j = 6 ; float x = 6 ; float y ; y = i / j * x ; // (int / int) * float -> int * float printf ("y vale %f\n", y) ; // ^^^^^^^^^ divisão inteira! y = x * i / j ; // (float * int) / int printf ("y vale %f\n", y) ; // resultado correto (vide abaixo) } Ao avaliar primeiro a operação ''i/j'', ocorre um erro de arredondamento: $ ./a.out y vale 96.000000 y vale 100.000000 As expressões são usualmente avaliadas da esquerda para a direita, mas isso depende da precedência dos operadores, além do compilador e do nível de otimização usado. Por isso, a ordem de avaliação das operações em uma expressão envolvendo tipos distintos deve ser explicitada usando **parênteses**: y = (x * i) / j ; // (float * int) / int printf ("y vale %f\n", y) ; // resultado correto sempre A conversão implícita na atribuição também pode provocar perda de informação. O exemplo abaixo mostra algumas dessas situações: #include int main () { char c ; int i ; long l ; float f ; double d ; c = 'A' ; i = c ; // ok, pois int > char printf ("c = %d, i = %d\n", c, i) ; i = 34 ; f = i ; // ok, pois float > int printf ("i = %d, f = %f\n", i, f) ; l = 214748364347243 ; f = l ; // perda de precisão printf ("l = %ld, f = %f\n", l, f) ; l = 214748364347243 ; d = l ; // ok, precisão suficiente printf ("l = %ld, d = %lf\n", l, d) ; f = 451.28 ; i = f ; // parte fracionária é truncada printf ("f = %f, i = %d\n", f, i) ; d = 3.141592653589793264 ; f = d ; // perda de precisão printf ("d = %.15f, f = %.15f\n", d, f) ; l = 12345677890 ; i = l ; // perda dos bits mais significativos printf ("l = %ld, i = %d\n", l, i) ; } Observe que a precisão de um ''float'' é menor que a de um ''int'' a partir de 223+1, pois a mantissa do ''float'' tem apenas 23 bits (padrão IEEE 754). Assim, no caso de um valor inteiro muito grande (> 223), a conversão do mesmo em ''float'' implica em perda de precisão. ===== 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 correta. Por exemplo: #include int main () { int soma, num ; float media ; soma = 10 ; num = 4 ; media = soma / num ; printf ("media = %f\n", media) ; } 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 (//type casting//). #include int main () { int soma, num ; float media ; soma = 10 ; num = 4 ; media = soma / num ; // errado, perda de precisão printf ("media = %f\n", media) ; media = 1.0 * soma / num ; // soma é "promovida" a float printf ("media = %f\n", media) ; media = (float) soma / num ; // soma é avaliada como float (casting) printf ("media = %f\n", media) ; media = soma / (float) num ; // num é avaliado como float (casting) printf ("media = %f\n", media) ; } 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 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 receber dados de diversos tipos. Como ponteiros para ''void'' não apontam para nenhum tipo válido de dado, eles não podem ser desreferenciados (ou seja, 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 #include #define VETSIZE 10 int vetor[VETSIZE] ; // compara dois inteiros apontados por "a" e "b" int compara_int (const void* a, const void* b) { int *pa, *pb ; pa = (int*) a ; // "vê" 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 for (i = 0; i < VETSIZE; i++) vetor[i] = random() % 1000 ; // escreve o vetor for (i = 0; i < VETSIZE; i++) printf ("%d ", vetor[i]) ; printf ("\n") ; // ordena o vetor (man qsort) // Protótipo: int (*compara_int) (const void *, const void *) qsort (vetor, VETSIZE, sizeof (int), compara_int) ; // escreve o vetor for (i = 0; i < VETSIZE; i++) printf ("%d ", vetor[i]) ; printf ("\n") ; } ===== Conversão de/para strings ===== No caso específico de strings, a conversão destas para outros tipos é efetuada através de funções específicas: #include // string to float, int, long, long long double atof (const char *str) ; int atoi (const char *str) ; long atol (const char *str) ; long long atoll (const char *str) ; // string to double, float, long double, com teste de erro double strtod (const char *nptr, char **endptr) ; float strtof (const char *nptr, char **endptr) ; long double strtold (const char *nptr, char **endptr) ; No sentido //número → string//, 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 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 ; } ===== Leitura complementar ===== * [[https://www.safaribooksonline.com/library/view/c-in-a/0596006977/ch04.html|Type Conversions, C in a Nutshell]].