Предупреждение C -Wformat для целочисленного продвижения в printf

Я использую 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);
                        ^

Если он буквально говорит вам, что вам нужен аргумент типа unsigned int, почему вы выполняете приведение к другому типу, чем unsigned int?

user743382 10.09.2018 04:10

Что касается того, почему приведение не сработало, предупреждение является результатом «целочисленных повышений», потому что uint16_t определяется целочисленным типом с рангом меньше, чем int в вашей системе (предположительно, потому что int имеет больший диапазон значений), поэтому преобразованное значение uint16_t в любом случае автоматически преобразуется в int. Выполните преобразование в unsigned int, как рекомендует компилятор.

user539810 10.09.2018 04:17

@hvd Ха-ха .. свое дело. Разве это не просто typedef? Такие термины, как "int", настолько расплывчаты, когда доступны такие особенности, как int32_t, поэтому я предпочел их использовать. Но в этом случае нет эквивалента uintXX_t "unsigned int".

Jetski S-type 10.09.2018 04:22

Компилятор сообщает, что ожидает unsigned int. Поэтому вместо этого вы выполняете трансляцию на uint16_t, который, вероятно, является unsigned short в вашей системе. Затем он повышается до int, когда передается в качестве аргумента без информации о типе в прототипе функции. Почему бы не сделать точно, что предлагает компилятор?

Tom Karzes 10.09.2018 04:23

@ JetskiS-type Здесь вы упускаете суть. Если вы используете любой typedef со встроенным в него определенным размером, то ваш код не будет переносимым, потому что, как только вы запустите его на машине с большим целым размером, вы снова получите подписанное значение.

Tom Karzes 10.09.2018 04:26

@TomKarzes Я привык использовать их, не задумываясь, так как раньше я встречал int, означающий разные вещи на разных платформах, а intXX_t - отличный способ быть конкретным. Это первый раз, когда использование формы intXX_t не сработало.

Jetski S-type 10.09.2018 04:28

@TomKarzes В самом деле, я сталкивался с проблемами переносимости спецификаторов формата printf раньше на 32-битных ARM и x86_64. Для печати uint32_t требуется "% u" на одном и "% lu" на другом (по памяти .. может быть неправильно). Но было бы неплохо, если бы спецификаторы printf соответствовали типам stdint.

Jetski S-type 10.09.2018 04:32

@ 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);

user539810 10.09.2018 04:35

@ChronoKitsune Это здорово, спасибо (хотя, к сожалению, немного некрасиво).

Jetski S-type 10.09.2018 04:40

@ JetskiS-type Согласен, поэтому я по возможности придерживаюсь примитивных типов. Еще одна причина, по которой я предпочитаю примитивы, заключается в том, что typedef заданного размера предназначены для использования только тогда, когда они вам нужны (например, структуры данных), а uint16_t точного размера может даже не существовать! Если вы работаете с системой, которая изначально использует 36 бит, uint18_t может существовать и работать медленнее, потому что любое значение меньше 36 бит на этой машине автоматически маскирует неиспользуемые биты. uint_least16_t может быть определен по типу того же типа, что и uint18_t, а uint_fast16_t может быть int для целей скорости.

user539810 10.09.2018 04:51

@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 - Reinstate Monica 10.09.2018 05:07

@chux См. мой первый ответ на hvd: «Ха-ха ... сделал свое дело». Избегать неопределенных типов int - для меня автоматическая привычка.

Jetski S-type 10.09.2018 05:38

Расплывчатый тип unsigned int всегда может содержать uint16_t.

Antti Haapala 10.09.2018 09:51
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
13
429
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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 The unsigned 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 =]

Jetski S-type 11.09.2018 01:54

@ 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 - Reinstate Monica 11.09.2018 02:09

@chux OP, скомпилированный с флагом -Wformat-signedness, который был добавлен в один из выпусков gcc 5.x (gcc 5.1 на Godbolt распознает его, но не gcc 4.9.4). Поведение флага в настоящее время не разрешено ни одним из значений, которые могут быть переданы в -Wformat, ни одним из флагов -pedantic / -Wpedantic, -Wextra или -Wall на любом уровне оптимизации.

user539810 11.09.2018 02:43

Тем не менее, PRIX16 в этом случае расширяется до "X", что приводит к "Value is 0x%" "X" "\n" как в моей системе Linux, так и в Godbolt. MSVC использует "hX", как и следовало ожидать. Не уверен, почему glibc использует "X" до PRIX64.

user539810 11.09.2018 02:43

@chux Не стесняйтесь - используйте проводник компилятора Godbolt самостоятельно! Также требуется -Wall в дополнение к -Wformat-signedness.

Jetski S-type 11.09.2018 03:05

@ JetskiS-type Чтобы быть справедливым по отношению к реализации, которую использует Godbolt gcc, предупреждение является ложным, если вы ориентируетесь на большинство (все?) Реализаций x86 и x86-64: диапазон от 0 до 65535 включительно имеет точно такое же битовое представление, независимо от того, интерпретируется как int или unsigned int. Другими словами, реализация, которую вы видите, не является неправильной сама по себе; предупреждение просто предполагает, что вы можете сделать свой код более портативный для реализаций, где int и unsigned int могут иметь разные битовые представления (например, значения битов заполнения могут отличаться в зависимости от подписи).

user539810 11.09.2018 03:36

Другие вопросы по теме