Спецификатор формата для unsigned char

Скажем, я хочу напечатать unsigned char:

unsigned char x = 12;

что является правильным. Это:

printf("%d",x);

или это:

printf("%u",x);

?

Дело в другом месте в SO Я столкнулся с таким обсуждением:

-Если с ch изменено на unsigned char, поведение кода не определяется стандартом C. Это связано с тем, что unsigned char продвигается до int (в нормальных реализациях C), поэтому int передается printf для спецификатора% u. Тем не менее,% u ожидает unsigned int, поэтому типы не совпадают, а стандарт C не определяет поведение

- Ваш комментарий неверен. В стандарте C11 указано, что спецификатор преобразования должен быть того же типа, что и сам аргумент функции, а не продвинутый тип. Этот пункт также специально рассматривается в описании модификатора длины hh: "аргумент будет продвигаться в соответствии с целыми рекламными акциями, но его значение должно быть преобразовано в подписанный char или без знака char перед печатью"

Итак, что правильно? Любой надежный источник, говорящий по этому поводу? (В этом смысле мы также должны печатать unsigned short int с% d, потому что его можно повысить до int?).

Ответ 1

Правильный - *:

printf("%d",x);

Это из-за ставок по умолчанию, поскольку printf() - это вариационная функция. Это означает, что значение unsigned char всегда увеличивается до int.

От N1570 (проект C11) 6.5.2.2/6 Функциональные вызовы (выделение в будущем):

Если выражение, обозначающее вызываемую функцию, имеет тип, который не включает прототип, целые акции выполняются на каждый аргумент и аргументы, имеющие тип float, double. Они называются рекламными акциями по умолчанию.

Под

и 6.5.2.2/7 указано:

Обозначение многоточия в объявлении прототипа функции вызывает преобразование типа аргумента для остановки после последнего объявленного параметра. Активные объявления аргументов по умолчанию выполняются при завершении аргументов.

Эти целые акции определяются в 6.3.1.1/2 булевых, символах и целых числах:

Если int может представлять все значения исходного типа (как ограниченный по ширине, для битового поля), значение преобразуется в значение int; в противном случае он преобразуется в unsigned int. Они называются целые рекламные акции .58) Все остальные типы не изменяются целым числом акции.

Эта цитата отвечает на ваш второй вопрос unsigned short (см. комментарий ниже).


* с исключением более 8 бит unsigned char (например, он может занимать 16 бит), см. @chux answer.

Ответ 2

Корректный спецификатор формата для unsigned char x = 12 зависит от нескольких вещей:

Если INT_MAX >= UCHAR_MAX, что часто бывает, используйте "%d". В этом случае a unsigned char продвигается до int.

printf("%d",x);

В противном случае используйте "%u" (или "%x", "%o"). В этом случае a unsigned char продвигается до unsigned.

printf("%u",x);

Современные компиляторы поддерживают модификатор длины "hh", который компенсирует эту неоднозначность. Если x получает повышение до int или unsigned из-за стандартных повышений вариационных параметров, printf() перед печатью преобразует его в unsigned char.

printf("%hhu",x);

Если вы работаете со старым компилятором без "hh" или ищете очень портативный код, используйте явное литье

printf("%u", (unsigned) x);

Такая же проблема/ответ применяется к unsigned short, ожидать INT_MAX >= USHRT_MAX и использовать "h" вместо "hh".

Ответ 3

Оба, unsigned char и unsigned short, всегда можно безопасно печатать с помощью %u. Активные объявления по умолчанию конвертируют их либо в int, либо в unsigned int. Если они продвинуты до последнего, все в порядке (спецификатор формата и тип, прошедший соответствие), в противном случае применяется C11 (n1570) 6.5.2.2 p6, первая пуля:

  • один продвинутый тип - это целочисленный тип со знаком, другой продвинутый тип - это соответствующий целочисленный тип без знака, и значение представляется в обоих типах;

Стандарт совершенно ясен, что объявления аргументов по умолчанию применяются к переменным аргументам printf, например. он упоминается снова для (в основном бесполезных) модификаторов длины h и hh (там же 7.21.6.1, p7, emph my):

hh - Указывает, что следующие d, i, o, u, x или x спецификации преобразования применяются к аргументу signed char или unsigned char аргумент будет продвигаться в соответствии с целыми рекламными акциями, но его значение должно быть преобразовано в signed char или unsigned char перед печатью); [...]

Ответ 4

Для кросс-платформенной разработки я обычно обходит проблему продвижения с помощью inttypes.h

http://pubs.opengroup.org/onlinepubs/009695399/basedefs/inttypes.h.html

Этот заголовок (который находится в стандарте C99) определяет все типы printf для базовых типов. Поэтому, если вам нужен uint8_t (синтаксис, который я настоятельно рекомендую использовать вместо unsigned char), я бы использовал

#include <inttypes.h>
#include <stdint.h>
uint8_t x;
printf("%" PRIu8 "\n",x);