По-перше, як це торкалося кількох інших відповідей, але, на мій погляд, не було прописано досить чітко: це працює, щоб забезпечити ціле число у більшості контекстів, де функція бібліотеки приймає аргумент 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
на основі специфікаторів, які ви використовуєте, і аргументів, які ви надаєте.