Зачем нужен литье в printf? - PullRequest
12 голосов
/ 29 ноября 2011

Для печати числа типа off_t было рекомендовано использовать следующий фрагмент кода:

off_t a;
printf("%llu\n", (unsigned long long)a);
  • Почему строка формата недостаточно недостаточно?
  • В чем будет проблема, если она не была приведена?

Ответы [ 3 ]

15 голосов
/ 29 ноября 2011

Строка формата не говорит компилятору выполнить приведение к 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, и они не зависят и не могут зависеть от строки формата.

4 голосов
/ 29 ноября 2011

Проблема в том, что вы не знаете, насколько велика off_t. Это может быть 64-битный тип или 32-битный тип (или, возможно, что-то еще). Если вы используете% llu и не передаете тип long (unsigned) long long, вы получите неопределенное поведение, на практике это может просто напечатать мусор.

Не зная, насколько он велик, проще всего привести его к наиболее разумному типу, поддерживаемому вашей системой, например неподписанный длинный длинный. Таким образом, использование% llu безопасно, так как printf получит тип unsigned long long из-за приведения.

(например, в linux размер off_t по умолчанию составляет 32 бита на 32-битной машине и 64 бита, если вы включаете поддержку больших файлов через #define _FILE_OFFSET_BITS=64 перед включением соответствующих системных заголовков)

1 голос
/ 29 ноября 2011

Подпись printf выглядит следующим образом:

int printf(const char *format, ...);

vararg ... указывает на то, что все может следовать, и по правилам C вы можете передать что-либо на printf до тех пор, покакак вы включаете формат строки.C просто не имеет никаких конструкций для описания каких-либо ограничений для типов передаваемых объектов.Вот почему вы должны использовать приведение, чтобы передаваемые объекты имели точно нужный тип.

Это типично для C, оно проходит границу между жесткостью и доверием программиста.Несоответствующим примером является то, что вы можете использовать char * (без const) для ссылки на строковые литералы, но если вы измените их, ваша программа может произойти сбой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...