Подтвердить что ты не робот

Использует printf ( "% x", 1) поведение undefined?

Согласно стандарту C (пункт 6.5.2.2, пункт 6)

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

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

Таким образом, в общем случае нет ничего плохого в передаче переменной int в переменную функцию, которая ожидает unsigned int (или наоборот), пока переданное значение подходит для обоих типов. Однако спецификация для printf гласит (7.19.6.1 пункт 9):

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

Не исключено исключение со знаком/без знака.

Означает ли это, что printf("%x", 1) вызывает поведение undefined?

4b9b3361

Ответ 1

Я считаю, что это технически undefined, потому что "правильный тип" для %x указан как unsigned int - и, как вы указываете, нет исключения для несоответствия с подписью/без знака здесь.

Правила для printf относятся к более конкретному случаю и, таким образом, переопределяют правила для общего случая (для другого примера специфического переопределения общего, его можно, вообще говоря, передать NULL функции, ожидающей a const char *, но это поведение undefined для перехода NULL в strlen()).

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

Ответ 2

Нет, потому что% x форматирует неподписанный int, а тип константного выражения 1 - int, а его значение выражается как unsigned int. Операция не UB.

Ответ 3

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

Единственная причина, по которой я вижу, что от подписанной до неподписанной переинтерпретации может быть ловушка, представляет собой этот извращенный случай представления знака, где беззнаковый тип просто маскирует знаковый бит. К сожалению, такая вещь допускается в соответствии с 6.2.6.2 стандарта. В такой архитектуре все отрицательные значения подписанного типа могут быть ловушками представления неподписанного типа.

В вашем примере это еще более странно, так как 1 представление ловушки для неподписанного типа, в свою очередь, не допускается. Поэтому, чтобы сделать его "реальным" примером, вам нужно задать свой вопрос с помощью -1.

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

Ответ 4

Я верю в это undefined. Функции с переменным аргументом переменной длины не имеют неявного преобразования при принятии аргументов, поэтому 1 не будет передаваться в unsigned int при прохождении до printf(), вызывая поведение undefined.

Ответ 5

Авторы Стандарта обычно не пытаются явно указать поведение во всех мыслимых случаях, особенно когда есть очевидное правильное поведение, которое разделяется 100% всех реализаций, и нет никаких оснований ожидать какой-либо реализации что-нибудь еще. Несмотря на явное требование Стандарта о том, что подписанные и неподписанные типы имеют соответствующие представления памяти для значений, которые подходят в обоих случаях, теоретически возможно, чтобы реализация передавала их вариационным функциям по-разному. Стандарт не запрещает такое поведение, но я не вижу никаких доказательств того, что авторы умышленно разрешают это. Скорее всего, они просто не рассматривали такую ​​возможность, поскольку никакая реализация никогда (и, насколько я знаю, никогда не работала), работала именно так.

Вероятно, было бы разумно, если бы санитационная реализация сквоила, если код использует% x по значению, подписанному, хотя реализация качественной санитарии также должна предоставить возможность молча воспринимать такой код. Нет никаких оснований для разумных реализаций делать что-либо другое, кроме как обрабатывать переданное значение как unsigned или squawk, если оно используется в режиме диагностики/дезинфекции. Хотя Стандарт может запретить реализацию из-за недостижимости любого кода, который использует% x по значению, подписанному, любой, кто считает, что реализации должны использовать такую ​​свободу, следует признать дебилом.

Программистам, которые нацелены исключительно на безупречные недиагностические реализации, не нужно беспокоиться о добавлении приводов при выводе таких вещей, как значения "uint8_t", но те, чей код может быть передан в модные реализации, могут захотеть добавить такие отбрасывания, чтобы предотвратить компиляторы из "оптимизаций", которые могут быть реализованы такие реализации.