Какие интегральные рекламные акции действительно имеют место при печати char?

Недавно я прочитал, что

unsigned char x=1;
printf("%u",x);

вызывает поведение undefined, поскольку из-за спецификатора формата% u printf ожидает беззнаковый int. Но все же я хотел бы понять, что происходит в этом примере.

Я думаю, что интегральные правила продвижения действуют с выражением printf("%u",x) и значение, представленное x.

A.6.1 Интеграционное продвижение

Символ, короткое целое или целочисленное битовое поле, все либо подписанные, либо нет, или объект типа перечисления, может использоваться в выражении везде, где может использоваться целое число. Если int может представлять все значения исходного типа, тогда значение преобразуется to int; в противном случае значение преобразуется в unsigned int. Этот процесс называется интегральным продвижение по службе.

Что означает "может быть использовано" здесь? Означает ли это, что "является синтаксически правильным" или "определено поведение"?

И как x продвигается в этом примере? Я прочитал, что он продвигается до int, но если printf("%u", (int x)) все еще undefined поведение, то я действительно не понимаю, почему...

Ответ 1

Так как printf использует список переменных аргументов, целочисленные рекламные акции применяются к его целым аргументам. В любой нормальной реализации C целые акции конвертируют unsigned char в int. Затем вы форматируете int С спецификатором для unsigned int, поэтому поведение undefined.

Нет никакого конфликта между тем, что можно использовать символ, где может использоваться целое число, и тот факт, что ваш оператор имеет поведение, не определенное стандартом C. Хотя вы можете использовать символ вместо целого числа, все же применяются правила о том, что можно напечатать с помощью %u. Если использование символа приводит к целому числу, соответствующему спецификатору, поведение определяется. Если использование символа приводит к тому, что целое число не подходит для спецификатора, поведение не определяется стандартом C.

Обсуждение в другом месте Qaru показало, что экзотическая реализация C может теоретически соответствовать стандарту C, имея char типы (простые, подписанные и неподписанные) в ширину, такие как int. В такой реализации и int не могли представлять все значения unsigned char, поэтому unsigned char нужно было бы повысить до unsigned int. Однако такая реализация будет экзотической и сложной (особенно с обработкой EOF), и вы можете игнорировать ее на практике.

Ответ 2

Если ваша платформа int может представлять все значения, чем unsigned char, то продвижение будет int, в противном случае - unsigned int. Это зависит от вашей платформы.

Что касается "почему", это потому, что вы передаете x в качестве аргумента переменной, а правила аргументов переменной говорят о том, что существуют стандартные рекламные акции (предположительно, чтобы упростить реализацию).

Ответ 3

Так как printf использует список переменных аргументов, он будет распакован через va_arg. С++ ссылается на стандарт C для правил va_arg. В стандарте C99 говорится следующее:

Макрос va_arg расширяется до выражения, имеющего специфицированный тип и значение следующего аргумента в вызове. Параметр ap должен быть инициализирован макросом va_start или va_copy (без промежуточного вызова макроса va_end для того же ap). Каждое обращение макроса va_arg изменяет ap так, что значения последовательных аргументов возвращаются поочередно. Тип параметра должен быть именем типа, указанным таким образом, чтобы тип указателя на объект, имеющий указанный тип, можно получить просто путем постфиксации типа *. Если фактический следующий аргумент отсутствует или тип несовместим с типом фактического следующего аргумента (в соответствии с продвижением по умолчанию), поведение undefined, за исключением следующие случаи:

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

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

Так как x = 1, безусловно, является значением, представляемым unsigned int, а продвижение unsigned char генерирует либо signed int (if INT_MAX >= UCHAR_MAX), либо unsigned int (if INT_MAX < UCHAR_MAX), это совершенно законно.