Ferramentas do usuário

Ferramentas do site


prog2:conversao_de_tipos

Diferenças

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

Link para esta página de comparações

Ambos lados da revisão anterior Revisão anterior
prog2:conversao_de_tipos [2019/05/01 12:07]
maziero
prog2:conversao_de_tipos [2019/05/01 12:10] (atual)
maziero [Conversão de/para strings]
Linha 1: Linha 1:
 +====== Conversão de tipos ======
  
 +Muitas vezes, operações lógicas/​aritméticas envolvem valores 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 operação lógica/​aritmética envolve dois operandos de tipos distintos, o compilador primeiro os converte para um único tipo, antes de avaliá-la. Nos caso de tipos numéricos, essa conversão é definida pela regra de **promoção automática**,​ segundo a qual o tipo de tamanho menor (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
 +
 +O código a seguir mostra alguns exemplos de promoção automática:​
 +
 +<code c>
 +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 de char para int
 +
 +y = i * x + k ; // ao avaliar i*x,       i é promovido de int a float
 +                // ao avaliar float + k, k é promovido de short a float
 +</​code>​
 +
 +A conversão implícita (ou automática) é realizada para as operações aritméticas (''​+ - * / %''​),​ relacionais (''<​ <= > >= == !=''​),​ com bits (''&​ | ^''​) e para o operador condicional (''?​ :''​).
 +
 +Os operadores de atribuição (''​='',​ ''​=+'',​ ...) também recebem uma conversão implícita de tipo, para compatibilizar o valor atribuído (lado direito da atribuição) à variável que está sendo atribuída (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) ;  //  ^^^^^^^^^ erro de arredondamento!
 +
 +  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 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. Eis alguns exemplos:
 +
 +<code c>
 +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 ; // geralmente ok, pois float > int (* vide abaixo)
 +i = y ; // problema, parte fracionária será truncada
 +x = pi ; // problema, perda de precisão
 +i = k ; // problema, perderá os bits mais significativos
 +</​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>
 +int soma, num ;
 +float media ;
 +
 +soma = 100 ;
 +num  = 40 ;
 +
 +media = soma / num ; // media = 2.0 (por que?)
 +</​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.
 +
 +<code c>
 +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)
 +</​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/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:​
 +
 +<code c>
 +#include <​stdlib.h>​
 +
 +double ​   atof  (const char *str); // string to float (double)
 +
 +int       ​atoi ​ (const char *str); // string to integer
 +
 +long      atol  (const char *str); // string to long
 +
 +long long atoll (const char *str); // string to long long 
 +</​code>​
 +
 +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:
 +
 +<code c conversion.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>​
 +
 +===== 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>​
 +
 +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 ;  // "​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
 +  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"​) ;
 +}
 +</​code>​
 +
 +Leitura complementar:​ [[https://​www.safaribooksonline.com/​library/​view/​c-in-a/​0596006977/​ch04.html|Type Conversions,​ C in a Nutshell]].
prog2/conversao_de_tipos.txt · Última modificação: 2019/05/01 12:10 por maziero