Diferenças

Aqui você vê as diferenças entre duas revisões dessa página.

Link para esta página de comparações

c:conversao_de_tipos [2023/08/01 18:10] – criada mazieroc:conversao_de_tipos [2023/08/01 20:16] (atual) – edição externa 127.0.0.1
Linha 1: Linha 1:
 +====== 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
 +
 +<note important>
 +A regra de promoção automática é aplicada separadamente **A CADA OPERAÇÃO** da expressão lógica/aritmética.
 +</note>
 +
 +O código a seguir mostra alguns exemplos de promoção automática:
 +
 +<code c>
 +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
 +</code>
 +
 +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:
 +
 +<code c erro-conversao.c>
 +#include <stdio.h>
 +
 +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)
 +}
 +</code>
 +
 +Ao avaliar primeiro a operação ''i/j'', ocorre um erro de arredondamento:
 +
 +<code>
 +$ ./a.out 
 +y vale 96.000000
 +y vale 100.000000
 +</code>
 +
 +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**:
 +
 +<code c>
 +y = (x * i) / j ;            // (float * int) / int
 +printf ("y vale %f\n", y) ;  // resultado correto sempre
 +</code>
 +
 +A conversão implícita na atribuição também pode provocar perda de informação. O exemplo abaixo mostra algumas dessas situações:
 +
 +<code c perdas.c>
 +#include <stdio.h>
 +
 +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) ;
 +}
 +</code>
 +
 +<note important>
 +Observe que a precisão de um ''float'' é menor que a de um ''int'' a partir de 2<sup>23</sup>+1, pois a mantissa do ''float'' tem apenas 23 bits (padrão IEEE 754). Assim, no caso de um valor inteiro muito grande (> 2<sup>23</sup>), a conversão do mesmo em ''float'' implica em perda de precisão. 
 +</note>
 +
 +===== 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:
 +
 +<code c media-erro.c>
 +#include <stdio.h>
 +
 +int main ()
 +{
 +  int soma, num ;
 +  float media ;
 +
 +  soma = 10 ;
 +  num  = 4 ;
 +
 +  media = soma / num ;
 +
 +  printf ("media = %f\n", media) ;
 +}
 +</code>
 +
 +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//).
 +
 +<code c media.c>
 +#include <stdio.h>
 +
 +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) ;
 +}
 +</code>
 +
 +<note tip>
 +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.
 +</note>
 +
 +===== 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)'':
 +
 +<code c qsort.c>
 +#include <stdio.h>
 +#include <stdlib.h>
 +
 +#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") ;
 +}
 +</code>
 +
 +===== 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:
 +
 +<code c>
 +#include <stdlib.h>
 +
 +// 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) ;
 +</code>
 +
 +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:
 +
 +<code c float2string.c>
 +#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 ;
 +}
 +</code>
 +
 +===== Leitura complementar =====
 +
 +  * [[https://www.safaribooksonline.com/library/view/c-in-a/0596006977/ch04.html|Type Conversions, C in a Nutshell]].