Primeiro, como tocou em várias outras respostas, mas não, na minha opinião, enunciado de forma suficientemente clara: Ele faz o trabalho para fornecer um número inteiro na maioria dos contextos em que uma função de biblioteca tem um double
ou float
argumento. O compilador irá inserir automaticamente uma conversão. Por exemplo, sqrt(0)
é bem definido e se comportará exatamente como sqrt((double)0)
, e o mesmo é verdadeiro para qualquer outra expressão do tipo inteiro usada lá.
printf
é diferente. É diferente porque leva um número variável de argumentos. Seu protótipo de função é
extern int printf(const char *fmt, ...);
Portanto, quando você escreve
printf(message, 0);
o compilador não tem nenhuma informação sobre que tipo printf
espera que o segundo argumento seja. Ele tem apenas o tipo da expressão do argumento, ou seja int
, para seguir. Portanto, ao contrário da maioria das funções de biblioteca, cabe a você, o programador, garantir que a lista de argumentos corresponda às expectativas da string de formato.
(Compiladores modernos podem olhar para uma string de formato e dizer que você tem uma incompatibilidade de tipo, mas eles não vão começar a inserir conversões para realizar o que você pretendia, porque melhor seu código deve quebrar agora, quando você perceber , do que anos depois, quando reconstruído com um compilador menos útil.)
Agora, a outra metade da pergunta era: Dado que (int) 0 e (float) 0,0 são, na maioria dos sistemas modernos, ambos representados como 32 bits, todos sendo zero, por que não funciona mesmo assim, por acidente? O padrão C diz apenas "isso não é necessário para funcionar, você está sozinho", mas deixe-me explicar os dois motivos mais comuns pelos quais não funcionaria; isso provavelmente ajudará você a entender por que não é obrigatório.
Primeiro, por razões históricas, quando você passa um float
por uma lista de argumentos de variável, ele é promovido para double
, que, na maioria dos sistemas modernos, tem 64 bits de largura. Portanto, printf("%f", 0)
passa apenas 32 bits zero para um receptor que espera 64 deles.
A segunda razão, igualmente significativa, é que os argumentos da função de ponto flutuante podem ser passados em um lugar diferente dos argumentos inteiros. Por exemplo, a maioria das CPUs tem arquivos de registro separados para valores inteiros e de ponto flutuante, então pode ser uma regra que os argumentos de 0 a 4 vão nos registros r0 a r4 se forem inteiros, mas f0 a f4 se forem de ponto flutuante. Portanto, printf("%f", 0)
procura no registro f1 por esse zero, mas ele não está lá.
printf
está esperando umdouble
, e você está dando umint
.float
eint
pode ter o mesmo tamanho em sua máquina, mas0.0f
na verdade é convertido em umdouble
quando colocado em uma lista de argumentos variável (eprintf
espera isso). Resumindo, você não está cumprindo sua parte da barganha comprintf
base nos especificadores que está usando e nos argumentos que está fornecendo.