Как правильно интерпретировать это предупреждение компилятора? - PullRequest
0 голосов
/ 27 июня 2018

Когда я выполнил код этого вопроса , я получил это предупреждение:

warning: format '%d' expects argument of type 'int', but argument 2 has type 'long int' [-Wformat=]
printf("P-Q: %d, P: %d, Q: %d", (p - q), p, q);
             ~^                 ~~~~~~~
             %ld

В качестве исправления рефлекса я использовал %ld, чтобы напечатать вычитание двух указателей. И компилятор согласился.

К счастью, я увидел комментарий другого пользователя, в котором говорилось, что следует использовать %td, так как тип результата вычитания - ptrdiff_t. Этот ответ подтверждает это утверждение.

Теперь из заголовочного файла GCC stddef.h я вижу, что эти типы эквивалентны в этом случае:

typedef __PTRDIFF_TYPE__ ptrdiff_t;
#define __PTRDIFF_TYPE__ long int

Однако я просто собирался предложить неправильное (более или менее) исправление для OP с %ld вместо %td.

Могу ли я понять, что одного предупреждения компилятора недостаточно? Или, может быть, разумно истолковал само предупреждение, а не просто отреагировал.

Ответы [ 2 ]

0 голосов
/ 27 июня 2018

Ключевой момент здесь: не делайте никакой формы арифметики внутри printf. Отдельный алгоритм от GUI.

Код, такой как printf("%d", p - q), очень опасен не только потому, что вы можете ошибочно интерпретировать типы, но также и потому, что C может «сделать вам одолжение» и молча изменять типы посредством неявного продвижения типов. Примеры .

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

Как избежать ошибок? Эти функции по своей природе опасны - так оно и есть, и все это знают. Вероятно, семейство функций printf & scanf - самые вредные функции, когда-либо написанные в истории программирования, с точки зрения общей стоимости ошибок, причиненных человечеству. Итак, что вы должны сделать, чтобы:

  • По возможности избегайте stdio.h и держите его подальше от производственного кода качества. Переносимость не всегда важнее, чем надежный код - иногда предпочтительнее использовать сырой консольный API. Избегайте функций списка переменных аргументов в целом.
  • Если избежать этого невозможно, оберните часть "101" * GUI внутри отдельного файла, что вам в любом случае следует делать. Не смешивайте печать / ввод с алгоритмами. Создайте интерфейс, использующий указатели.
  • Это 2018, а не 1970: в первую очередь не пишите консольные интерфейсы. Вы, я знаю ... много старого дерьма, все еще плавающего вокруг, которое нужно поддерживать. Но в настоящее время консольные функции следует использовать в основном для целей отладки и для обучения новичков C, и в этом случае безопасность типов не может быть такой большой проблемой.
0 голосов
/ 27 июня 2018

Я не думаю, что вы можете сказать. Это зависит от намерения / осторожности / сообразительности автора компилятора.

Возможно, он решил, что всегда будет поддерживать %ld, где ожидается %td, или, может быть, он просто не знал / не мог / не хотел давать более подробное / правильное сообщение. В случае сомнений, ваше последнее средство является стандартом.

Это не переносимая конструкция, и для "ортодоксальности" вы должны поддерживать оба спецификатора формата.

...