Вызывает ли printf ("% x", 1) неопределенное поведение? - PullRequest
31 голосов
/ 12 января 2011

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

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

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

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

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

Нет исключений для несоответствия со знаком / без знака.

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

Ответы [ 5 ]

15 голосов
/ 12 января 2011

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

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

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

8 голосов
/ 12 января 2011

Нет, поскольку% x форматирует беззнаковое целое, а тип константного выражения 1 - это целое число, а его значение можно выразить как беззнаковое целое.Операция не UB.

3 голосов
/ 12 января 2011

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

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

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

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

0 голосов
/ 16 июня 2017

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

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

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

0 голосов
/ 15 марта 2016

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

...