Из Википедии, бесплатной энциклопедии
Перейти к навигации Перейти к поиску

Пример функции printf

Строка формата printf относится к параметру управления, используемому классом функций в библиотеках ввода / вывода C и многих других языков программирования . Строка написана на простом языке шаблонов : символы обычно буквально копируются в выходные данные функции, но спецификаторы формата , начинающиеся с %символа, указывают местоположение и метод перевода части данных (например, числа) в символы.

«printf» - это имя одной из основных функций вывода языка C, которое означает « print f ormatted». Строки формата printf дополняют строки формата scanf , которые обеспечивают форматированный ввод ( синтаксический анализ ). В обоих случаях они обеспечивают простую функциональность и фиксированный формат по сравнению с более сложными и гибкими механизмами шаблонов или синтаксическими анализаторами, но их достаточно для многих целей.

Многие языки, отличные от C, копируют синтаксис строки формата printf точно или точно в свои собственные функции ввода-вывода.

Несоответствие между спецификаторами формата и типом данных может вызвать сбои и другие уязвимости. Сама строка формата очень часто является строковым литералом , что позволяет статический анализ вызова функции. Однако это также может быть значение переменной, допускающее динамическое форматирование, а также уязвимость системы безопасности, известная как эксплойт неконтролируемой строки формата .

История [ править ]

Ранние языки программирования, такие как Fortran, использовали специальные операторы с совершенно отличным от других вычислений синтаксисом для построения описаний форматирования. В этом примере формат указан в строке 601, а команда WRITE обращается к нему по номеру строки:

  ЗАПИСЬ ВЫХОД  TAPE  6 ,  601 ,  И.А. ,  IB ,  IC ,  ПЛОЩАДЬ 601  ФОРМАТ  ( 4 Н  = , I5 , 5 Н В = , I5 , 5 Н С = , I5 , и 8 Н ОБЛАСТЬ = , F10 . 2 , 13 Н КВАДРАТНЫЕ ЕДИНИЦЫ )            

Алгол 68 имеет более функциональный API , но по-прежнему использует специальный синтаксис ( $разделители окружают специальный синтаксис форматирования):

printf (( $ "Color" g ", number1" 6 d , ", number2" 4 zd , ", hex" 16 r2d , ", float" - d .2 d , ", значение без знака" -3 d "." l $ ,  "красный" ,  123456 ,  89 ,  БИН  255 ,  3.14 ,  250 ));

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

Кассиопеяне printfимеет свои истоки в BCPL «S writefфункции (1966). По сравнению с Cи printf, *Nэто escape-последовательность языка BCPL , представляющая символ новой строки (для которого C использует escape-последовательность \n), а порядок ширины и типа поля спецификации формата меняется на противоположный writef: [1]

WRITEF ("ПРОБЛЕМА% I2-QUEENS ИМЕЕТ% I5 РЕШЕНИЙ * N"; NUMQUEENS, COUNT)

Вероятно, первым копированием синтаксиса за пределами языка C была команда printfоболочки Unix , которая впервые появилась в версии 4 как часть переноса на C. [2]

Спецификация заполнителя формата [ править ]

Форматирование происходит через заполнители в строке формата. Например, если программа хотела распечатать возраст человека, она могла бы представить результат, поставив перед ним префикс «Ваш возраст» и используя знаковый десятичный спецификатор, dчтобы обозначить, что мы хотим, чтобы целое число для возраста отображалось немедленно. после этого сообщения мы можем использовать строку формата:

printf ( "Ваш возраст% d" ,  возраст );

Синтаксис [ править ]

Синтаксис заполнителя формата:

% [ параметр ] [ флаги ] [ ширина ] [. точность ] [ длина ] тип

Поле параметра [ править ]

Это расширение POSIX, а не C99 . Поле параметра может быть опущено или может быть:

Эта функция в основном используется при локализации, где порядок появления параметров зависит от языка.

В Microsoft Windows, отличной от POSIX, поддержка этой функции помещена в отдельную функцию printf_p.

Поле флагов [ править ]

В поле Flags может быть ноль или более (в любом порядке):

Поле ширины [ править ]

Поле «Ширина» определяет минимальное количество символов для вывода и обычно используется для заполнения полей фиксированной ширины в табличном выводе, где в противном случае поля были бы меньше, хотя это не приводит к усечению слишком больших полей.

Поле ширины может быть опущено, числовое целочисленное значение или динамическое значение при передаче в качестве другого аргумента, если оно обозначено звездочкой * . [3] Например, в результате будет напечатана общая ширина 5 символов.printf("%*d", 5, 10) 10

Хотя это и не часть ширины поля, ведущий нуль интерпретируются как флаг нулевого отступа упоминался выше, и значение отрицательного рассматриваются как положительное значение в сочетании с левым выравниванием - флаг также упомянутым выше.

Поле точности [ править ]

В поле «Точность» обычно указывается максимальный предел вывода в зависимости от конкретного типа форматирования. Для числовых типов с плавающей запятой он определяет количество цифр справа от десятичной запятой, для которых выходные данные должны быть округлены. Для строкового типа он ограничивает количество выводимых символов, после чего строка усекается.

Поле точности может быть опущено, числовое целочисленное значение или динамическое значение при передаче в качестве другого аргумента, если оно обозначено звездочкой * . Например, приведет к печати.printf("%.*s", 3, "abcdef")abc

Поле длины [ править ]

Поле длины может быть опущено или иметь одно из следующих значений:

Кроме того, до широкого использования расширений ISO C99 появилось несколько вариантов длины для конкретных платформ:

ISO C99 включает inttypes.hфайл заголовка, который включает ряд макросов для использования в платформенно-независимом printfкодировании. Они должны быть вне двойных кавычек, напримерprintf("%" PRId64 "\n", t);

Примеры макросов включают:

Поле типа [ править ]

Поле Тип может быть любым из:

Заполнители произвольного формата [ править ]

Существует несколько реализаций printf-подобных функций, которые позволяют расширять мини-язык на основе escape-символов , что позволяет программисту иметь специальную функцию форматирования для не встроенных типов. Одним из наиболее хорошо известных является ( в настоящее время не рекомендуется) Glibc «s . Однако он редко используется из-за того, что конфликтует с проверкой строки статического формата. Другой - настраиваемые форматеры Vstr , которые позволяют добавлять имена многосимвольных форматов. register_printf_function()

Некоторые приложения (например, HTTP-сервер Apache ) включают в себя свои собственные printf-подобные функции и встраивают в них расширения. Однако все они, как правило, имеют те же проблемы register_printf_function().

Функция ядра Linux printk поддерживает несколько способов отображения структур ядра с использованием общей %pспецификации путем добавления дополнительных символов формата. [8] Например, %pI4выводит IPv4- адрес в десятичной форме с точками. Это позволяет проверять строку статического формата ( %pчасти) за счет полной совместимости с обычным printf.

Большинство языков, в которых есть printf-подобная функция, обходят недостаток этой функции, просто используя %sформат и преобразовывая объект в строковое представление.

Уязвимости [ править ]

Недействительные спецификации преобразования [ править ]

Если предоставлено слишком мало аргументов функции для предоставления значений для всех спецификаций преобразования в строке шаблона, или если аргументы не правильного типа, результаты будут неопределенными, что может привести к сбою. Реализации не согласуются с тем, используют ли синтаксические ошибки в строке аргумент и какой тип аргумента они используют. Лишние аргументы игнорируются. В ряде случаев неопределенное поведение приводило к уязвимостям системы безопасности « Атака форматной строки » . В большинстве соглашений о вызовах C или C ++ аргументы могут передаваться в стек, что означает, что в случае слишком малого числа аргументов printf будет читать за пределами текущего стекового фрейма, что позволяет злоумышленнику читать стек.

Некоторые компиляторы, такие как GNU Compiler Collection , будут статически проверять строки формата функций, подобных printf, и предупреждать о проблемах (при использовании флагов -Wallили -Wformat). GCC также будет предупреждать об определяемых пользователем функциях в стиле printf, если к функции применяется нестандартный «формат» __attribute__.

Ширина поля по сравнению с явными разделителями в табличном выводе [ править ]

Использование только ширины полей для табуляции, как в случае формата %8d%8d%8dтрех целых чисел в трех 8-символьных столбцах, не гарантирует, что разделение полей будет сохранено, если в данных встречаются большие числа. Отсутствие разделения полей может легко привести к повреждению вывода. В системах, которые поощряют использование программ в качестве строительных блоков в сценариях, такие поврежденные данные часто могут быть перенаправлены и повредят дальнейшую обработку, независимо от того, ожидал ли исходный программист, что результат будет прочитан только человеческими глазами. Такие проблемы можно устранить, включив явные разделители, даже пробелы, во все табличные форматы вывода. Просто изменив опасный пример с предыдущего на%7d %7d %7dрешает эту проблему, форматируя одинаково, пока числа не станут больше, но затем явно предотвращает их объединение при выводе из-за явно включенных пробелов. Аналогичные стратегии применимы к строковым данным.

Запись в память [ править ]

Хотя функция вывода находится на поверхности, она printfпозволяет записывать в ячейку памяти, указанную аргументом через %n. Эта функция иногда используется как часть более сложных атак на строку формата. [9]

Эта %nфункциональность также делает printfслучайным Тьюринг завершенным даже с хорошо сформированным набором аргументов. Игра в крестики-нолики, написанная в формате строки, стала победителем 27-го IOCCC . [10]

Языки программирования с printf [ править ]

Языки, которые используют строки формата, которые отличаются от стиля, описанного в этой статье (например, AMPL и Elixir ), языки, которые наследуют свою реализацию от JVM или другой среды (например, Clojure и Scala ), и языки, не имеющие стандартного собственного printf реализация, но есть внешние библиотеки, имитирующие поведение printf (например, JavaScript ), не включены в этот список.

  • awk (через sprintf)
  • C
    • С ++ (также перегружены операторами сдвига и манипуляторы в качестве альтернативы для форматированного вывода - см iostream и iomanip )
    • Цель-C
  • D
  • F #
  • G ( LabVIEW )
  • GNU MathProg
  • GNU Octave
  • Идти
  • Haskell
  • J
  • Java (начиная с версии 1.5) и языки JVM
  • Джулия (через его стандартную библиотеку Printf; [11] библиотека Formatting.jl добавляет общее форматирование в стиле Python и «часть этого пакета в стиле c направлена ​​на то, чтобы обойти ограничение, заключающееся в том, что @sprintf должен принимать буквальный строковый аргумент»).
  • Lua (string.format)
  • Клен
  • MATLAB
  • Макс (через объект sprintf)
  • Мифрил
  • PARI / GP
  • Perl
  • PHP
  • Python (через %оператор) [12]
  • р
  • Рака (через printf, sprintfи fmt)
  • Красный / Система
  • Рубин
  • Tcl (через команду форматирования)
  • Transact-SQL (через xp_sprintf )
  • Вала (через print()и FileStream.printf())
  • printfУтилита командный, иногда встроен в оболочку, например, с некоторыми реализациями KornShell (КШ), Борн снова оболочки (Bash) или Z оболочка (ЗШ). Эти команды обычно интерпретируют escape-символы C в строке формата.

См. Также [ править ]

  • Формат (Common Lisp)
  • Стандартная библиотека C
  • Атака на форматную строку
  • iostream
  • ML (язык программирования)
  • printf отладка
  • printf (Unix)
  • printk (распечатать сообщения ядра)
  • scanf
  • строковая интерполяция

Ссылки [ править ]

  1. ^ "BCPL" . cl.cam.ac.uk . Проверено 19 марта 2018 .
  2. Перейти ↑ McIlroy, MD (1987). Читатель Research Unix: аннотированные выдержки из Руководства программиста, 1971–1986 (PDF) (технический отчет). CSTR. Bell Labs. 139.
  3. ^ "printf - Справочник по C ++" . cplusplus.com . Проверено 10 июня 2020 .
  4. ^ ISO / IEC (1999). ISO / IEC 9899: 1999 (E): Языки программирования - C §7.19.6.1, параграф 7
  5. ^ " " Справочное руководство по библиотеке GNU C "," Таблица преобразования вывода 12.12.3 " " . Gnu.org . Проверено 17 марта 2014 года .
  6. ^ "printf" ( % a добавлено в C99)
  7. ^ «Форматирование числового вывода на печать» . Учебники по Java . Oracle Inc . Проверено 19 марта 2018 .
  8. ^ "Документация по ядру Linux / printk-sizes.txt" . Git.kernel.org . Проверено 17 марта 2014 года .
  9. ^ https://www.exploit-db.com/docs/english/28476-linux-format-string-exploitation.pdf
  10. ^ https://www.ioccc.org/2020/carlini/index.html
  11. ^ "Стандартная библиотека Printf" . Руководство по языку Julia . Проверено 22 февраля 2021 года .
  12. ^ «Встроенные типы: форматирование строк в стиле printf » , Стандартная библиотека Python, Python Software Foundation , получено 24 февраля 2021 г.

Внешние ссылки [ править ]

  • Справочник по C ++ для std::fprintf
  • Краткий справочник спецификаций формата gcc printf
  • printf: вывод с форматированием для печати - Справочник по системным интерфейсам, Единая спецификация UNIX , Выпуск 7 от The Open Group
  • FormatterСпецификации в Java 1.5
  • printf(1)Встроенный GNU Bash