En primer lugar, como tocado en varias otras respuestas, pero no, en mi opinión, explicado con suficiente claridad: Se hace el trabajo para proporcionar un número entero en la mayoría de los contextos en los que una función de biblioteca toma una double
o float
argumento. El compilador insertará automáticamente una conversión. Por ejemplo, sqrt(0)
está bien definido y se comportará exactamente como sqrt((double)0)
, y lo mismo ocurre con cualquier otra expresión de tipo entero que se use allí.
printf
es diferente. Es diferente porque requiere un número variable de argumentos. Su función prototipo es
extern int printf(const char *fmt, ...);
Por tanto, cuando escribes
printf(message, 0)
el compilador no tiene ninguna información sobre qué tipo printf
espera que sea ese segundo argumento. Solo tiene el tipo de expresión de argumento, que es int
, para pasar. Por lo tanto, a diferencia de la mayoría de las funciones de la biblioteca, depende de usted, el programador, asegurarse de que la lista de argumentos coincida con las expectativas de la cadena de formato.
(Los compiladores modernos pueden buscar en una cadena de formato y decirle que tiene una falta de coincidencia de tipos, pero no van a comenzar a insertar conversiones para lograr lo que quería decir, porque mejor su código debería romperse ahora, cuando lo notará , que años después cuando se reconstruyó con un compilador menos útil).
Ahora, la otra mitad de la pregunta era: dado que (int) 0 y (float) 0.0 están, en la mayoría de los sistemas modernos, ambos representados como 32 bits, todos los cuales son cero, ¿por qué no funciona de todos modos, por accidente? El estándar C simplemente dice "esto no es necesario para funcionar, estás solo", pero déjame explicarte las dos razones más comunes por las que no funcionaría; eso probablemente le ayudará a comprender por qué no es necesario.
Primero, por razones históricas, cuando se pasa a float
través de una lista de argumentos variables, se asciende a la double
que, en la mayoría de los sistemas modernos, tiene 64 bits de ancho. Por lo tanto, printf("%f", 0)
pasa solo 32 bits cero a un destinatario que espera 64 de ellos.
La segunda razón, igualmente significativa, es que los argumentos de la función de punto flotante pueden pasarse en un lugar diferente al de los argumentos enteros. Por ejemplo, la mayoría de las CPU tienen archivos de registro separados para enteros y valores de punto flotante, por lo que podría ser una regla que los argumentos del 0 al 4 vayan en los registros r0 a r4 si son enteros, pero de f0 a f4 si son de punto flotante. Entonces printf("%f", 0)
busca en el registro f1 ese cero, pero no está allí en absoluto.
printf
está esperando undouble
, y le está dando unint
.float
yint
puede ser del mismo tamaño en su máquina, pero en0.0f
realidad se convierte en adouble
cuando se inserta en una lista de argumentos variadic (yprintf
espera eso). En resumen, no está cumpliendo con su parte del trato enprintf
función de los especificadores que utiliza y los argumentos que proporciona.