Какова цель модификаторов h и hh для printf? - PullRequest
52 голосов
/ 03 января 2011

Помимо %hn и %hhn (где h или hh указывает размер объекта , на который указывает ), какова точка h и hh модификаторы для printf спецификаторов формата?

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

Согласно 7.19.6.1 (7), модификатор h:

Указывает, что следующий спецификатор преобразования d, i, o, u, x или X применяется к короткий int или неподписанный короткий int аргумент (аргумент будет были продвинуты в соответствии с целочисленными акциями, но его значение быть преобразованным в short int или unsigned short int перед печатью); или что следующий n спецификатор преобразования применяется к указателю на короткий int аргумент.

Если аргумент на самом деле был типа short или unsigned short, то повышение до int с последующим преобразованием обратно в short или unsigned short приведет к тому же значению , что и повышение на int без конвертации обратно. Таким образом, аргументы типа short или unsigned short, %d, %u и т. Д. Должны давать идентичные результаты для %hd, %hu и т. Д. (И аналогично для char типов и * 1039). *).

Насколько я могу судить, единственная ситуация, когда модификатор h или hh может быть полезен, - это когда аргумент передал ему значение int вне диапазона short или unsigned short, например

printf("%hu", 0x10000);

но я понимаю, что передача неправильного типа, подобного этому, в любом случае приводит к неопределенному поведению, так что вы не можете ожидать, что он напечатает 0.

Один случай из реальной жизни, который я видел, - это такой код:

char c = 0xf0;
printf("%hhx", c);

, где автор ожидает, что он напечатает f0, несмотря на то, что реализация имеет простой тип char со знаком (в этом случае printf("%x", c) будет печатать fffffff0 или подобное). Но оправдано ли это ожидание?

(Примечание. То, что происходит, заключается в том, что исходный тип был char, который переводится в int и преобразуется обратно в unsigned char вместо char, что приводит к изменению значения, которое выводится на печать. стандарт определяет это поведение, или это детали реализации, на которые может полагаться сломанное программное обеспечение?)

Ответы [ 7 ]

14 голосов
/ 03 января 2011

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

Хотя в них не упоминается важность симметрии для модификаторов "h" и "hh" в Обоснование C99 , комитет упоминает это как соображение, почему спецификатор преобразования "% p" поддерживается для fscanf() (хотя это не было новым для C99 - поддержка "% p" в C90):

Преобразование входного указателя с% p было добавлено в C89, хотя это очевидно рискованно, для симметрии с fprintf.

В разделе на fprintf()Обоснование C99 действительно обсуждает, что было добавлено «hh», но просто отсылает читателя к разделу fscanf():

Модификаторы длины% hh и% ll были добавлены в C99 (см. §7.19.6.2).

Я знаю, что это неровная тема, но я все равно размышляю, поэтому я решил, что приведу любой аргумент.

Кроме того, для полноты,модификатор "h" был в оригинальном стандарте C89 - presuвозможно, он был бы там, даже если бы он не был строго необходим из-за широко распространенного существующего использования, даже если бы не было технических требований для использования модификатора.

5 голосов
/ 04 января 2011

Единственное, о чем я могу подумать, - это передать unsigned short или unsigned char и использовать спецификатор преобразования %x. Вы не можете просто использовать голое %x - значение может быть увеличено до int вместо unsigned int, и тогда у вас будет неопределенное поведение.

Вы можете либо явно привести аргумент к unsigned; или использовать %hx / %hhx с пустым аргументом.

5 голосов
/ 03 января 2011

В режиме %...x все значения интерпретируются как беззнаковые. Отрицательные числа поэтому печатаются как их беззнаковые преобразования. В арифметике дополнения 2, которую использует большинство процессоров, нет различий в битовых комбинациях между отрицательным числом со знаком и его положительным беззнаковым эквивалентом, который определяется арифметикой модуля (добавление максимального значения для поля плюс один к отрицательному числу, согласно к стандарту C99). Множество программного обеспечения, особенно код отладки, который, скорее всего, будет использовать %x, делает молчаливое предположение, что битовое представление отрицательного значения со знаком и его беззнакового преобразования одинаковы, что справедливо только для компьютера с дополнением 2.

Механика этого приведения такова, что шестнадцатеричные представления значения всегда подразумевают, возможно, неточно, что число было представлено в дополнении 2, пока оно не достигло граничного условия, где различные целочисленные представления имеют разные диапазоны. Это справедливо даже для арифметических представлений, где значение 0 не представлено двоичным шаблоном всех 0.

Отрицательный short, отображаемый как unsigned long в шестнадцатеричном формате, поэтому на любом компьютере будет дополнен f из-за неявного расширения знака в рекламной акции, которое будет напечатано printf. Значение такое же, но оно действительно вводит в заблуждение относительно размера поля, подразумевая значительный диапазон диапазона, которого просто нет.

%hx усекает отображаемое представление, чтобы избежать этого заполнения, точно так же, как вы сделали вывод из своего реального варианта использования.

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

Если printf не дополняет значения или не отображает беззнаковые представления значений со знаком, %h не очень полезен.

1 голос
/ 27 декабря 2017

Я считаю полезным избегать приведения при форматировании неподписанных символов в шестнадцатеричные:

        sprintf_s(tmpBuf, 3, "%2.2hhx", *(CEKey + i));

Это незначительное удобство кодирования и выглядит чище, чем многократное приведение (IMO).

1 голос
/ 03 января 2011

Аргументы variadic для printf() и др. Автоматически повышаются с использованием преобразований по умолчанию, поэтому любые значения short или char переводятся в int при передаче функции.

В отсутствие модификаторов h или hh вам придется маскировать передаваемые значения, чтобы надежно получить правильное поведение. С модификаторами вам больше не нужно маскировать значения; printf() реализация делает работу правильно.

В частности, для формата %hx код внутри printf() может делать что-то вроде:

va_list args;
va_start(args, format);

...

int i = va_arg(args, int);
unsigned short s = (unsigned short)i;
...print s correctly, as 4 hex digits maximum
...even on a machine with 64-bit `int`!

Я беспечно предполагаю, что short является 16-битной величиной; Стандарт, конечно, не гарантирует этого.

0 голосов
/ 01 января 2018

еще одно удобное место - проверка размера snprintf. gcc7 добавлена ​​проверка размера при использовании snprintf так что это не удастся

char arr[4];
char x='r';
snprintf(arr,sizeof(arr),"%d",r);

, поэтому он заставляет вас использовать больший символ при использовании% d при форматировании символа

Вот коммит, который показывает эти исправления вместо увеличения размера массива символов, который они изменили,% d на% h это также дает более точное описание

https://github.com/Mellanox/libvma/commit/b5cb1e34a04b40427d195b14763e462a0a705d23#diff-6258d0a11a435aa372068037fe161d24

0 голосов
/ 04 января 2011

Я согласен с вами, что это не является строго необходимым, и поэтому только по этой причине не годится функция библиотеки C:)

Это может быть "хорошо" для симметрии различных флагов, но это в основном контрпродуктивно, поскольку скрывает правило "преобразования в int".

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