Я использую GCC 5.2.1 с ARM Cortex A9 и компилирую с -std = c11 и -Wformat-signedness.
Как мне избежать предупреждения -Wformat в этом случае?
int main()
{
enum
{
A = 0,
B
};
char buff[100];
snprintf(buff, 100, "Value is 0x%04x\n", A);
return 0;
}
Это вызывает предупреждение:
format '%x' expects argument of type 'unsigned int', but argument 4 has
type 'int' [-Werror=format=]
snprintf(buff, 100, "Value is 0x%04x\n", A);
^
Явное приведение дает тот же результат:
format '%x' expects argument of type 'unsigned int', but argument 4 has
type 'int' [-Werror=format=]
snprintf(buff, 100, "Value is 0x%04x\n", (uint16_t)A);
^
Что касается того, почему приведение не сработало, предупреждение является результатом «целочисленных повышений», потому что uint16_t
определяется целочисленным типом с рангом меньше, чем int
в вашей системе (предположительно, потому что int
имеет больший диапазон значений), поэтому преобразованное значение uint16_t
в любом случае автоматически преобразуется в int
. Выполните преобразование в unsigned int
, как рекомендует компилятор.
@hvd Ха-ха .. свое дело. Разве это не просто typedef? Такие термины, как "int", настолько расплывчаты, когда доступны такие особенности, как int32_t, поэтому я предпочел их использовать. Но в этом случае нет эквивалента uintXX_t "unsigned int".
Компилятор сообщает, что ожидает unsigned int
. Поэтому вместо этого вы выполняете трансляцию на uint16_t
, который, вероятно, является unsigned short
в вашей системе. Затем он повышается до int
, когда передается в качестве аргумента без информации о типе в прототипе функции. Почему бы не сделать точно, что предлагает компилятор?
@ JetskiS-type Здесь вы упускаете суть. Если вы используете любой typedef со встроенным в него определенным размером, то ваш код не будет переносимым, потому что, как только вы запустите его на машине с большим целым размером, вы снова получите подписанное значение.
@TomKarzes Я привык использовать их, не задумываясь, так как раньше я встречал int, означающий разные вещи на разных платформах, а intXX_t - отличный способ быть конкретным. Это первый раз, когда использование формы intXX_t не сработало.
@TomKarzes В самом деле, я сталкивался с проблемами переносимости спецификаторов формата printf раньше на 32-битных ARM и x86_64. Для печати uint32_t требуется "% u" на одном и "% lu" на другом (по памяти .. может быть неправильно). Но было бы неплохо, если бы спецификаторы printf соответствовали типам stdint.
@ JetskiS-type Стандартный заголовок <inttypes.h>
предоставляет макросы для семейств printf
и scanf
, такие как PRIx16
для печати значения uint16_t
(хотя само значение по-прежнему будет преобразовано компилятором в int
, если необходимо), и SCNxFAST16
для чтения значения uint_fast16_t
. . Например, snprintf(buff, 100, "Value is 0x%04" PRIx16 "\n", (uint16_t)A);
@ChronoKitsune Это здорово, спасибо (хотя, к сожалению, немного некрасиво).
@ JetskiS-type Согласен, поэтому я по возможности придерживаюсь примитивных типов. Еще одна причина, по которой я предпочитаю примитивы, заключается в том, что typedef заданного размера предназначены для использования только тогда, когда они вам нужны (например, структуры данных), а uint16_t
точного размера может даже не существовать! Если вы работаете с системой, которая изначально использует 36 бит, uint18_t
может существовать и работать медленнее, потому что любое значение меньше 36 бит на этой машине автоматически маскирует неиспользуемые биты. uint_least16_t
может быть определен по типу того же типа, что и uint18_t
, а uint_fast16_t
может быть int
для целей скорости.
@etski S-type До сих пор непонятно: почему snprintf(buff, 100, "Value is 0x%04x\n", (uint16_t)A);
вместо snprintf(buff, 100, "Value is 0x%04x\n", (unsigned)A);
?
@chux См. мой первый ответ на hvd: «Ха-ха ... сделал свое дело». Избегать неопределенных типов int - для меня автоматическая привычка.
Расплывчатый тип unsigned int
всегда может содержать uint16_t
.
How do I avoid a -Wformat warning in this case?
Приведите перечислимый тип к unsigned
, чтобы он соответствовал "%x"
.
// snprintf(buff, 100, "Value is 0x%04x\n", A);
snprintf(buff, 100, "Value is 0x%04x\n", (unsigned) A);
o,u,x,X
Theunsigned int
argument is converted to ... C11 §7.21.6.1 8
Если код приводит к чему-то другому, кроме unsigned
, по какой-то причине, используйте указанный соответствующий спецификатор печати. @Chrono Kitsune
#include <inttypes.h>
// snprintf(buff, 100, "Value is 0x%04x\n", (uint16_t)A);
snprintf(buff, 100, "Value is 0x%04" PRIX16 "\n", (uint16_t)A);
Мораль истории: используйте соответствующие спецификаторы печати с каждым аргументом.
Строка должна быть: «Значение 0x%» PRIX16 «\ n», однако даже это не работает: предупреждение godbolt.org/z/gyhStF: формат '% X' ожидает аргумент типа 'unsigned int', но аргумент 4 имеет тип 'int' [ -Wformat =]
@ JetskiS-type Пожалуйста, сообщите 1) puts(PRIX16);
(я бы ожидал "hX"
) и 2) Вам может потребоваться более новая версия компилятора GCC 5.2.1 -> 7.3.0, поскольку я не могу воспроизвести предупреждение с помощью '-Wpedantic' '-Wall' '-Wextra' '-Wconversion' '-c' '-fmessage-length=0' '-Wformat=1'
.
@chux OP, скомпилированный с флагом -Wformat-signedness
, который был добавлен в один из выпусков gcc 5.x (gcc 5.1 на Godbolt распознает его, но не gcc 4.9.4). Поведение флага в настоящее время не разрешено ни одним из значений, которые могут быть переданы в -Wformat
, ни одним из флагов -pedantic
/ -Wpedantic
, -Wextra
или -Wall
на любом уровне оптимизации.
Тем не менее, PRIX16
в этом случае расширяется до "X"
, что приводит к "Value is 0x%" "X" "\n"
как в моей системе Linux, так и в Godbolt. MSVC использует "hX"
, как и следовало ожидать. Не уверен, почему glibc использует "X"
до PRIX64
.
@chux Не стесняйтесь - используйте проводник компилятора Godbolt самостоятельно! Также требуется -Wall в дополнение к -Wformat-signedness.
@ JetskiS-type Чтобы быть справедливым по отношению к реализации, которую использует Godbolt gcc, предупреждение является ложным, если вы ориентируетесь на большинство (все?) Реализаций x86 и x86-64: диапазон от 0 до 65535 включительно имеет точно такое же битовое представление, независимо от того, интерпретируется как int
или unsigned int
. Другими словами, реализация, которую вы видите, не является неправильной сама по себе; предупреждение просто предполагает, что вы можете сделать свой код более портативный для реализаций, где int
и unsigned int
могут иметь разные битовые представления (например, значения битов заполнения могут отличаться в зависимости от подписи).
Если он буквально говорит вам, что вам нужен аргумент типа
unsigned int
, почему вы выполняете приведение к другому типу, чемunsigned int
?