Почему gcc -Wformat не предупреждает о printf %d для беззнакового целого числа?

Следующая программа имеет неопределенное поведение:

#include <stdio.h>

int main(void)
{
    unsigned int x = -100; // This is fine, becomes UINT_MAX - 100
    printf("%d\n", x); // This is undefined behavior.
    return 0;
}

C99 7.19.6.1p8 указывает, что %d ожидает аргумент типа int.

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

Однако gcc -Wformat (входящий в состав -Wall) не будет жаловаться на указанную выше программу, почему? Это ошибка или преднамеренное упущение?

Из справочной страницы gcc:

-Wformat

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


person Chris Young    schedule 03.07.2012    source источник
comment
C99 6.3.1.3p3 говорит, что преобразование беззнакового в подписанное определяется реализацией.   -  person jxh    schedule 03.07.2012
comment
@ user315052: преобразования нет; представление x (объект unsigned int) интерпретируется так, как если бы оно было типа int.   -  person Keith Thompson    schedule 03.07.2012
comment
@KeithThompson: я думаю, что это из-за C99 7.15.1.1p2, последнее предложение, где оно делает исключение для подписанных/беззнаковых при преобразовании типов аргументов с помощью макроса va_arg.   -  person jxh    schedule 03.07.2012
comment
@KeithThompson в коде не происходит преобразования только потому, что функция имеет тип varargs. Если бы функция была объявлена ​​принимающей int, вызов был бы законным. Так почему же следует ожидать, что gcc выдаст предупреждение о судебном разбирательстве?   -  person Andy Ross    schedule 03.07.2012
comment
@ user315052: я только что прочитал этот абзац; это не означает, что произошла конверсия.   -  person Keith Thompson    schedule 03.07.2012
comment
@AndyRoss: gcc, конечно, необязателен для выдачи предупреждения, но он может сделать это разумно, поскольку, если значение x превышает INT_MAX, поведение не определено. Аргументы типа int и unsigned int взаимозаменяемы только для значений, представленных в обоих типах.   -  person Keith Thompson    schedule 03.07.2012
comment
@KeithThompson: это отрицает, что это неопределенное поведение. Таким образом, это должно быть одно из определено, не указано или определено реализацией. Весь раздел был посвящен преобразованию типов через va_arg. Поскольку стандарт никак не определяет поведение, кроме преобразования беззнакового в подписанное, я пришел к выводу, что реализация определяется посредством этого преобразования. Но, YMMV.   -  person jxh    schedule 03.07.2012
comment
@user315052: user315052: он определен, если один тип является целым числом со знаком, другой тип является соответствующим целочисленным типом без знака, и значение может быть представлено в обоих типах; в примере UINT_MAX-100 превышает INT_MAX. И в разделе ничего не говорится о конверсиях.   -  person Keith Thompson    schedule 03.07.2012
comment
@KeithThompson: Понятно, спасибо.   -  person jxh    schedule 03.07.2012
comment
Компиляторы не обязаны предупреждать обо всем, что является UB в стандарте. Если этот компилятор написан таким образом, что в этом случае он ведет себя хорошо, то проблем нет.   -  person M.M    schedule 14.06.2016


Ответы (1)


Насколько я понимаю, предупреждение пропущено, потому что UB, возможно, вызывается по значению, а не просто по типу. va_arg допускает несоответствие подписанности, если значение может быть представлено как в подписанном, так и в беззнаковом типе. Однако printf и друзья не указаны в терминах va_arg, и в стандарте указано, что любое несоответствие типов приводит к UB, но это, вероятно, ошибка в стандарте. В противном случае printf("%x",1); вызовет UB. Смотрите мой вопрос по теме:

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

person R.. GitHub STOP HELPING ICE    schedule 03.07.2012
comment
Спасибо. Это имеет смысл, так как 6.2.5p6 требует, чтобы int и unsigned int использовали одинаковый объем памяти. Даже несмотря на то, что printf %d для неподписанного файла технически не определен, нет никаких правдоподобных причин, по которым это может вызвать реальные проблемы. - person Chris Young; 03.07.2012
comment
В стандарте не говорится, что printf() использует <stdarg.h>, но тот факт, что вы можете сконструировать указатель на функцию printf(), которую можно вызывать через него, подразумевает, по крайней мере, некоторую общность в механизме передачи аргументов. - person Keith Thompson; 03.07.2012
comment
GCC 5.0 теперь имеет такое предупреждение: -Wformat-signedness (включено -Wformat) . - person cremno; 18.03.2015
comment
экспериментируя здесь rextester.com/live/CCQJKP79309 предполагает, что -Wformat-signedness работает, но не включен в - Wформат - person MK.; 04.04.2017
comment
Весь смысл отдельной опции в том, что это не так. - person R.. GitHub STOP HELPING ICE; 04.04.2017