Строка формата не говорит компилятору выполнить приведение к unsigned long long
, она просто сообщает printf
, что она получит unsigned long long
.Если вы передадите что-то, что не и unsigned long long
(которого off_t
не может быть), то printf
просто неверно истолкует это, с удивительными результатами.
Причина этогов том, что компилятору не нужно ничего знать о строках формата.Хороший компилятор выдаст вам предупреждающее сообщение, если вы напишите printf("%d", 3.0)
, но что может сделать компилятор, если вы напишите printf(s, 3.0)
, где s
- это строка, определяемая динамически во время выполнения?
Отредактировано, чтобы добавить: Как отмечает Кит Томпсон в комментариях ниже, во многих местах компилятор может выполнять такого рода неявное преобразование.printf
довольно исключительный случай, когда не может .Но если вы объявите функцию для приема unsigned long long
, то компилятор будет выполнит преобразование:
#include <stdio.h>
#include <sys/types.h>
int print_llu(unsigned long long ull)
{
return printf("%llu\n", ull); // O.K.; already converted
}
int main()
{
off_t a;
printf("%llu\n", a); // WRONG! Undefined behavior!
printf("%llu\n", (unsigned long long) a); // O.K.; explicit conversion
print_llu((unsigned long long) a); // O.K.; explicit conversion
print_llu(a); // O.K.; implicit conversion
return 0;
}
Причина этого в том, что printf
объявлен как int printf(const char *format, ...)
, где ...
является нотацией «variadic» или «variable-arguments», сообщая компилятору, что он может принимать любое число и типы аргументов после format
.(Очевидно, printf
не может на самом деле принимать любое количество и типы аргументов: он может принимать только те числа и типы, которые вы ему сообщаете, используя format
. Но компилятор ничего не знаетоб этом; программисту предстоит справиться с этим.)
Даже с ...
компилятор делает некоторые неявные преобразования, такие как повышение char
в int
и float
в double
.Но эти преобразования не являются специфичными для printf
, и они не зависят и не могут зависеть от строки формата.