По-перше, як це торкалося кількох інших відповідей, але, на мій погляд, не було прописано досить чітко: це працює, щоб забезпечити ціле число у більшості контекстів, де функція бібліотеки приймає аргумент doubleабо floatаргумент. Компілятор автоматично вставить перетворення. Наприклад, sqrt(0)є чітко визначеним і буде поводитися точно так само sqrt((double)0), і те саме стосується будь-якого іншого цілочисельного виразу, що використовується там.
printfінакший. Це інакше, оскільки для цього потрібно змінювати кількість аргументів. Його функціональним прототипом є
extern int printf(const char *fmt, ...);
Тому, коли ви пишете
printf(message, 0)
компілятор не має жодної інформації про те, якого типу printf очікується другий аргумент. Він має лише тип виразу аргументу, тобто intпройти. Тому, на відміну від більшості функцій бібліотеки, саме вам, програмісту, слід переконатись, що список аргументів відповідає очікуванням рядка форматування.
(Сучасні компілятори можуть розглянути рядок формату та повідомити, що у вас невідповідність типу, але вони не збираються починати вставляти перетворення, щоб виконати те, що ви мали на увазі, тому що краще ваш код повинен зламатися зараз, коли ви помітите , ніж роками пізніше, коли перебудований за допомогою менш корисного компілятора.)
Тепер інша половина запитання полягала в тому, що, враховуючи, що (int) 0 та (float) 0.0 у більшості сучасних систем обидва представлені як 32 біти, всі з яких дорівнюють нулю, чому це все одно не працює, випадково? Стандарт С просто говорить: "це не потрібно для роботи, ти сам на собі", але дозвольте мені пояснити дві найпоширеніші причини, чому це не спрацювало; це, мабуть, допоможе вам зрозуміти, чому це не потрібно.
По- перше, з історичних причин, коли ви проходите floatчерез змінний список аргументів він отримує підвищений до double, який, в більшості сучасних систем, становить 64 біт. Отже, printf("%f", 0)передає лише 32 нульові біти абоненту, який очікує 64 з них.
Друга, не менш важлива причина полягає в тому, що аргументи функції з плаваючою комою можуть передаватися в іншому місці, ніж цілі аргументи. Наприклад, більшість процесорів мають окремі регістрові файли для цілих чисел і значень з плаваючою комою, тому може бути правилом, що аргументи від 0 до 4 надходять у регістри від r0 до r4, якщо вони цілі числа, але від f0 до f4, якщо вони мають плаваючу крапку. Тож printf("%f", 0)шукає в реєстрі f1 цей нуль, але його взагалі немає.
printfочікуєdouble, а ви йому даєтеint.floatіintможе мати однаковий розмір на вашій машині, але0.0fнасправді перетворюється на аdoubleпри натисканні у варіантний список аргументів (іprintfочікує цього). Коротше кажучи, ви не виконуєте свій кінець угодиprintfна основі специфікаторів, які ви використовуєте, і аргументів, які ви надаєте.