Спецификатор формата для 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);