stringtranslate.com

печать

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

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

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

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

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

История

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

  ЗАПИСЬ ВЫХОДНОЙ ЛЕНТЫ 6 , 601 , IA , IB , IC , AREA 601 ФОРМАТ ( 4 H A = , I5 , 5 H B = , I5 , 5 H C = , I5 , & 8 H AREA = , F10 . 2 , 13 H КВАДРАТНЫЕ ЕДИНИЦЫ )                      

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

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

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

Язык C printfберет свое начало в функции BCPLwritef (1966). По сравнению с Cи является escape-последовательностью языка BCPL printf, представляющей символ новой строки (для которого C использует escape-последовательность ), а порядок ширины и типа поля спецификации формата изменен на обратный : [1]*N\nwritef

WRITEF("Проблема %I2-QUEENS ИМЕЕТ %I5 РЕШЕНИЙ*N", NUMQUEENS, COUNT)

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

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

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

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

Синтаксис

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

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

Поле параметра

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

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

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

Поле флагов

Поле Флаги может содержать ноль или более (в любом порядке):

Поле ширины

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

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

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

Прецизионное поле

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

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

Поле длины

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

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

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

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

Тип поля

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

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

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

Некоторые приложения (например, HTTP-сервер Apache ) включают в себя собственную printfфункцию -like и встраивают в нее расширения. Однако все они, как правило, имеют одни и те же проблемы 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-символьных столбцах, не гарантирует, что разделение полей будет сохранено, если в данных встречаются большие числа:

1234567 1234567 1234567123 123 123 123 12345678123 

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

1234567 1234567 1234567123 123 123 123 12345678 123 

Аналогичные стратегии применяются к строковым данным.

Запись в память

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

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

Языки программирования с помощью printf

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

Смотрите также

Рекомендации

  1. Ссылки cl.cam.ac.uk. ​Проверено 19 марта 2018 г.
  2. ^ Макилрой, доктор медицины (1987). Читатель Research Unix: аннотированные выдержки из Руководства программиста, 1971–1986 (PDF) (Технический отчет). CSTR. Лаборатории Белла. 139.
  3. ^ "принтф". cplusplus.com . Проверено 10 июня 2020 г.
  4. ^ ИСО / МЭК (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 . Оракл Инк . Проверено 19 марта 2018 г.
  8. ^ "Документация ядра Linux/printk-formats.txt" . Git.kernel.org. Архивировано из оригинала 29 апреля 2015 года . Проверено 17 марта 2014 г.
  9. ^ https://www.exploit-db.com/docs/english/28476-linux-format-string-exploitation.pdf [ пустой URL-адрес PDF ]
  10. ^ «Лучшее из шоу - злоупотребление libc» . Ioccc.org . Проверено 5 мая 2022 г.
  11. ^ «» Базовые спецификации открытой группы, выпуск 7, издание 2018 г.», «POSIX awk», «Выходные операторы»» . pubs.opengroup.org . Проверено 29 мая 2022 г.
  12. ^ "Стандартная библиотека Printf" . Руководство по языку Julia . Проверено 22 февраля 2021 г.
  13. ^ «Встроенные типы: форматирование строк в стиле printf», Стандартная библиотека Python , Python Software Foundation , получено 24 февраля 2021 г.

Внешние ссылки