Какова цель модификаторов h и hh для printf?

помимо %hn и %hhn (где h или hh задает размер указал на object), в чем смысл h и hh модификаторы для

7 ответов


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

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

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

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

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

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

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


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

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

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

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

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

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


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

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


вариативные аргументы для printf() et al автоматически продвигаются с использованием конверсий по умолчанию, поэтому любой 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-битное количество; стандарт на самом деле не гарантирует этого, конечно.


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

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

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


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

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


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

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

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

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

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