Tout d'abord, comme évoqué dans plusieurs autres réponses mais pas, à mon avis, assez clairement expliqué: cela fonctionne pour fournir un entier dans la plupart des contextes où une fonction de bibliothèque prend un argument doubleou float. Le compilateur insérera automatiquement une conversion. Par exemple, sqrt(0)est bien défini et se comportera exactement comme sqrt((double)0), et il en va de même pour toute autre expression de type entier utilisée ici.
printfest différent. C'est différent car il prend un nombre variable d'arguments. Son prototype de fonction est
extern int printf(const char *fmt, ...);
Par conséquent, lorsque vous écrivez
printf(message, 0);
le compilateur n'a aucune information sur le type printf attendu de ce second argument. Il n'a que le type de l'expression d'argument, qui est int, pour passer. Par conséquent, contrairement à la plupart des fonctions de bibliothèque, c'est à vous, le programmeur, de vous assurer que la liste d'arguments correspond aux attentes de la chaîne de format.
(Les compilateurs modernes peuvent examiner une chaîne de format et vous dire que vous avez une incompatibilité de type, mais ils ne vont pas commencer à insérer des conversions pour accomplir ce que vous vouliez dire, car mieux votre code devrait se casser maintenant, quand vous le remarquerez , que des années plus tard lors de la reconstruction avec un compilateur moins utile.)
Maintenant, l'autre moitié de la question était: étant donné que (int) 0 et (float) 0.0 sont, sur la plupart des systèmes modernes, tous deux représentés comme 32 bits qui sont tous à zéro, pourquoi ne fonctionne-t-il pas de toute façon, par accident? La norme C dit simplement "cela n'est pas nécessaire pour fonctionner, vous êtes seul", mais laissez-moi vous expliquer les deux raisons les plus courantes pour lesquelles cela ne fonctionnerait pas; cela vous aidera probablement à comprendre pourquoi ce n'est pas nécessaire.
Tout d' abord, pour des raisons historiques, lorsque vous passez une floatpar une liste d'arguments variable , il se promu à doublequi, sur la plupart des systèmes modernes, est 64 bits. Donc printf("%f", 0)passe seulement 32 bits de zéro à un appelé qui en attend 64.
La deuxième raison, tout aussi significative, est que les arguments de fonction à virgule flottante peuvent être passés à un endroit différent des arguments entiers. Par exemple, la plupart des processeurs ont des fichiers de registre séparés pour les entiers et les valeurs à virgule flottante, il se peut donc que les arguments 0 à 4 soient placés dans les registres r0 à r4 s'ils sont des entiers, mais f0 à f4 s'ils sont à virgule flottante. printf("%f", 0)Cherche donc dans le registre f1 ce zéro, mais il n'y est pas du tout.
printfattend undouble, et vous lui donnez unint.floatetintpeut être de la même taille sur votre machine, mais0.0fest en fait converti en undoublelorsqu'il est poussé dans une liste d'arguments variadiques (et il s'yprintfattend). En bref, vous ne remplissez pas votre part du marché enprintffonction des spécificateurs que vous utilisez et des arguments que vous fournissez.