Являются ли предупреждения компилятора printf/sprintf концептуальным разрывом?
Я заметил, что большое количество компиляторов Си выдают предупреждения, когда спецификаторы преобразования в строке формата функций printf / sprintf не соответствуют типу или количеству соответствующих аргументов.
Это кажется мне концептуальным прорывом, поскольку C не имеет встроенных функций в соответствии со спецификацией языка.
Все, что должен знать компилятор о printf / sprintf, это их прототипы, а не их семантика. Я знаю, что printf / sprintf являются стандартными функциями C, но все же они находятся в отдельной библиотеке libc, и вы должны включить stdio.h для импорта их прототипов.
Вместо этого многие компиляторы анализируют строку формата, которая также может быть предоставлена во время выполнения.
Имеет ли вышесказанное смысл?
7 ответов
"Все, что должен знать компилятор о printf/sprintf, это их прототипы, а не их семантика".
Это та часть, которая не соответствует действительности. Что касается стандарта, то любой части реализации C "разрешено" знать о любой другой части и выпускать диагностику, которая может быть полезна для пользователя. Встроенные функции компилятора не требуются стандартом, как и эта конкретная диагностика, но они, конечно, не запрещены.
Обратите внимание, что (что касается стандарта) стандартная библиотека является особенной, это не просто старая связанная библиотека. Если конкретная реализация / компилятор даже предоставляет пользователю механизм для связи с другой версией стандартной библиотеки, стандарт определенно не требует, чтобы он "работал", когда эта альтернативная библиотека имеет семантику, отличную от той, что изложена в стандарт.
Таким образом, в этом смысле все в стандартной библиотеке является "встроенным". Это часть спецификации языка Си. Компиляторам разрешено действовать в предположении, что они ведут себя так, как требует стандарт.
Конечно, если спецификатор формата не известен до времени выполнения, тогда компилятор не может выполнить статическую проверку varargs. Но когда это известно во время компиляции, компилятор может принять поведение printf
так же верно, как это может предполагать поведение memcpy
или целочисленного сложения.
Задача компилятора здесь просто дать вам несколько полезных советов. Такое поведение не охватывается стандартом.
Реализация может генерировать предупреждения во многих ситуациях, ни одна из которых не указана как часть этого международного стандарта.
Теоретически, ничто не мешает компилятору предупреждать вас (возможно) о неправильном использовании, скажем, библиотеки QT.
А также printf
является стандартной функцией в том смысле, что она (включая ее семантику) охватывается стандартом ISO C.
Если я правильно прочитал ваш вопрос, я согласен с вашей предпосылкой, что проверка printf
и строки формата друзей компилятором - это концептуальная деятельность, в отличие от других видов статической проверки (синтаксис, тип и т. д.), выполняемой компилятором.
Тем не менее, это разрешено стандартом и очень помогает нам, бедным программистам.
Стандарт требует диагностики при некоторых обстоятельствах, но не запрещает общую диагностику. Любая реализация может выдавать диагностическую информацию по любой причине, включая неправильное использование printf()
или чрезмерное использование буквы Q. Очевидно, что некоторые из этих причин более полезны, чем другие.
Более того, если вы включите библиотеку, все видимые идентификаторы в ней станут зарезервированными. Вам не разрешено #include <stdio.h>
и иметь свое собственное определение printf
(см. 7.1.3 проекта стандарта C99). Это означает, что реализация может свободно предполагать, что вы используете стандарт printf
и относиться к нему как к обязательной части стандарта.
Конечная цель стандартов языка программирования - помочь программистам писать программы, которые ведут себя так, как задумано. В стандарте нет ничего, что говорило бы, что компилятор должен выдать предупреждение, если он встречает "bigvar = byte3 << 24 + byte2 << 16 + byte1 << 8 + byte0;", но так как результаты, вероятно, не те, что программист Предполагается, что многие компиляторы выдают предупреждение. Единственное ограничение, которое стандарты накладывают на предупреждения, состоит в том, что они не должны препятствовать успешной компиляции легитимной программы (например, компилятора, который завершился с ошибкой после вывода 999 предупреждений, или который выдает так много предупреждений, что компиляция для всех практических целей никогда не завершать, будет не соответствующим).
Нет никакого требования, чтобы компилятор "знал" о стандартных библиотеках, но также нет требования, чтобы он не знал о них, если кто-то # включает нормальные заголовки. Действительно, если программа включает в себя
Такого рода предупреждения указывают на возможные ошибки и, как следствие, полезны.
Да, может показаться непоследовательным иметь специальные случаи для предупреждений в компиляторе (при условии <stdio.h>
не просто __printf_format_warning
атрибут или что-то в этом роде), но опять же, если это полезно и помогает решить некоторые ошибки (возможно, даже ошибки безопасности), то почему бы их не иметь?
Я имею в виду, что не все просто заменяют свои libc на свои, с другой семантикой printf...
Это концептуальный перерыв? Да. Но «концептуальный разрыв» в том , что для всех намерений и целей, C имеет теперь «встроенные функции». Они указаны в Стандарте, все их имена зарезервированы (если вы не находитесь в автономной среде), а компиляторы используют для них особый регистр всевозможными способами.
Например, если я позвоню, не включив должным образом
<stdio.h>
, Я обычно получаю предупреждение типа «несовместимое неявное объявление встроенной функции 'printf'» (gcc) или «неявное объявление библиотечной функции 'printf' с типом 'int (const char *, ...)'» (лязг). Оба сообщения подтверждают, что компилятор знал о
printf
все это время, независимо от того, явно ли я включил файл заголовка с внешним объявлением, которое что-то сообщило компилятору.
Учитывая, что компиляторы знают о библиотечных функциях, вполне уместно, что одна из вещей, которые они делают с этими знаниями, в случае с функциями printflike, - это перепроверить фактические аргументы.
Да, я помню те дни , когда библиотечные функции действительно не были построены, в любом случае, и есть вещи , о тех днях , что я скучаю, но на самом деле, тот факт , что составители этого теперь знают о библиотечных функциях не вызывает у меня какие-либо проблемы или стоившие мне сна.
И я твердо уверен, что для современного компилятора проверка количества и типов аргументов, передаваемых функциям printflike, является более или менее обязательной. В старые добрые времена, когда библиотечные функции не были частью языка, прототипов функций тоже не существовало. Если программист хотел убедиться, что вызов функции соответствует ее определению, он должен был либо быть осторожным, либо запустить lint. Но прототипы изменили это, и современные программисты знают, что можно называть, скажем,
sqrt(144)
. Но по той же причине современные программисты не понимают, почему они не могут позвонить
printf("%f\n", 144)
. И я действительно не могу винить в этом современных программистов.