paulo1205
(usa Ubuntu)
Enviado em 15/12/2015 - 05:31h
Thihup escreveu:
*No printf para imprimir double é usado o %lf , para imprimir float é utilizado o %f
Infelizmente , essa afirmação está errada.
Eu dou ênfase ao “infelizmente” porque realmente discordo da existência da regra, que me parece mais uma daquelas coisas socadas no padrão para acomodar compatibilidade com código antigo. A verdade, porém, é que em funções que recebem um número variável de argumentos, incluindo
printf () e suas irmãs, um dado do tipo
float que seja parte dos argumentos variáveis é sempre promovido a
double . Do mesmo modo, todo valor integral de tamanho menor ou igual ao tamanho de um inteiro (
bool ,
char ou
short ) é convertido para
int .
Assim, o
%f (ou
%e ,
%g ou
%a , bem como suas variantes em letra maiúscula) de
printf () indica um valor do tipo
double , sempre. A especificação
%lf (e variantes com
aAeEFgG )
também denotam
double (como o análogo em
scanf ()), mas é possível que o mais comum seja encontrar a formatação sem o “l”.
Note que a regra acima se aplica apenas aos argumentos na parte variável da lista de parâmetros; os que forem declarados com seus próprios tipos na assinatura da função continuam com as características que teriam variáveis comuns do mesmo tipo. Assim sendo, no código abaixo, ao ser invocada a função
f (), o primeiro argumento continuará sendo um
float dentro de
f (), mas o segundo terá se tornado um
double .
void f(float f, ...){
assert(sizeof f==sizeof(float)); // Vai funcionar
double d;
va_list params_d;
va_start(params_d, f);
d=va_arg(params_d, double);
assert(d==f); // Provavelmente vai funcionar (exceto problema de arredondamento).
float f2;
va_list params_f;
va_start(params_f, f);
f2=va_arg(params_f, float);
assert(f2==f); // Possivelmente vai falhar, a não ser que va_arg também converta float em double
}
void g(void){
float f1, f2;
f1=f2=1.23456789;
f(f1, f2); // f1 segue como float; f2 é convertido em double antes de chamar f()
}
Aliás, na hora de compilar aqui, o GCC chiou, mas deixou passar.
In file included from d.c:2:0:
d.c: In function ‘f’:
d.c:18:22: warning: ‘float’ is promoted to ‘double’ when passed through ‘...’ [enabled by default]
f2=va_arg(params_f, float);
^
d.c:18:22: note: (so you should pass ‘double’ not ‘float’ to ‘va_arg’)
d.c:18:22: note: if this code is reached, the program will abort
Em tempo, se você quiser trabalhar com
long double , o modificador da conversão será
L (maiúsculo).
*No scanf segue a mesma lógica do printf , mas na hora de passar as variáveis, é necessário colocar o & na frente da variável [exceto se estiver utilizando ponteiros].
Bem, como vimos acima, a lógica é um pouco diferente.
Lembrando um pouco dos fundamentos, todos os parâmetros de função em C são sempre passados por valor. Isso significa, por exemplo, que se eu tenho duas variáveis
a e
b de tipos quaisquer e uma função
f (), uma chamada do tipo
f(a,b) vai
copiar os valores de
a e de
b para uma região de memória que seja visível de dentro de
f (), e então invocar a execução de
f (). Como a função trabalha com cópias, qualquer alteração que ela faça sobre os valores que ela recebeu não se reflete nos dados originais.
Para que uma função consiga modificar valores que lhe são externos, a técnica que se usa em C é aplicar o operador unário
& ao dado que se quer modificar, pois essa operação devolve o endereço de onde o dado está armazenado (i.e. um ponteiro), e então passar o valor desse endereço como argumento para a função. Faz sentido: afinal, se um ponteiro aponta para um determinado lugar da memória, a cópia desse ponteiro aponta para a mesma posição. Dentro da função, então, utiliza-se o operador unário
* , que é faz o inverso do que faz
& , ou seja, chega a um dado não-temporário a partir do seu endereço e do seu tipo.
Sabendo isso, pode-se entender o porquê de
scanf () ter diferenças em relação a
printf (). Ambas possuem um primeiro parâmetro fixo, que diz como os demais, que têm forma variável (podendo até mesmo eventualmente não existir), devem ser interpretados. No entanto, os argumentos variáveis de
printf (), interpretados de acordo com a formatação, são
cópias dos valores dos dados, ao passo que para
scanf () eles são
cópias dos endereços em que os dados devem residir após operação de leitura bem sucedida.
Assim, não existe promoção de
float para
double (nem de
char para
int , nem qualquer outra) com
scanf (), simplesmente porque
scanf () nunca recebe um
float (ou
char , ou o que quer que seja), mas tão somente ponteiros para dados que podem ser desses tipos. Lá dentro da função é que, de acordo com a string de formatação, o argumento será interpretado como sendo um ponteiro para
float , e então o dado será manipulado de acordo com as regras específicas desse tipo, e gravado na respectiva região de memória referenciada pelo ponteiro.