Использует printf ( "% x", 1) поведение undefined?
Согласно стандарту C (пункт 6.5.2.2, пункт 6)
Если выражение, обозначающее вызываемую функцию, имеет тип, который не включает прототип, целые рекламные акции выполняются для каждого аргумента и аргументы, которые имеют тип float, которые удваиваются. Они называются аргументом по умолчанию Акции. Если число аргументов не равно числу параметров, поведение не определено. Если функция определена с типом, который включает прототип, и либо прототип заканчивается эллипсисом (,...) или типами аргументов после продвижение несовместимо с типами параметров, поведение не определено. Если функция определена с типом, который не включает прототип, а типы аргументы после продвижения по службе не совместимы с параметрами после продвижение, поведение не определено, за исключением следующих случаев:
- один продвинутый тип представляет собой целочисленный тип со знаком, другой способ продвижения - это соответствующий беззнаковый целочисленный тип, и значение представляется в обоих типах;
- оба типа являются указателями на квалифицированные или неквалифицированные версии типа символа или недействительно.
Таким образом, в общем случае нет ничего плохого в передаче переменной int
в переменную функцию, которая ожидает unsigned int
(или наоборот), пока переданное значение подходит для обоих типов. Однако спецификация для printf
гласит (7.19.6.1 пункт 9):
Если спецификация преобразования недействительна, поведение не определено. Если какой-либо аргумент не правильный тип для соответствующей спецификации преобразования, поведение недеформированной определено.
Не исключено исключение со знаком/без знака.
Означает ли это, что printf("%x", 1)
вызывает поведение undefined?
Ответы
Ответ 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", но те, чей код может быть передан в модные реализации, могут захотеть добавить такие отбрасывания, чтобы предотвратить компиляторы из "оптимизаций", которые могут быть реализованы такие реализации.