printf () без аргументов в C компилируется нормально. как?
я попробовал следующую программу c , и я ожидал получить ошибку времени компиляции, но почему компилятор не дает никакой ошибки?
#include <stdio.h>
int main(void)
{
printf("%dn");
return 0;
}
почему вывод зависит от компилятора? Вот вывод на различных компиляторах
вывод на Orwell Dev C++ IDE (использует gcc 4.8.1): 0
вывод на Visual C++, предоставленный Visual Studio 2010: 0
IDE CodeBlocks (использует gcc 4.7.1): значение мусора
онлайн-компилятор ideone.com : мусор значение
что здесь не так ?
10 ответов
ваша программа будет компилироваться нормально, как printf()
является вариационной функцией, и проверка соответствия количества спецификаторов формата с предоставленным аргументом по умолчанию не выполняется.
во время выполнения ваша программа показывает неопределено поведение, поскольку в аргументе нет аргумента, который должен быть напечатан с помощью спецификатора поставляемого формата.
в соответствии с главой 7.19.6.1, c99
стандартные (с fprintf()
)
если есть недостаточно аргументов для формата, поведение не определено.
если вы компилируете с помощью -Wformat
флаг gcc
, ваш компилятор выдаст предупреждение о несоответствии.
из-за того, как работают c variadic аргументы, компилятор не может отслеживать их правильное использование. По-прежнему (синтаксически) законно предоставлять меньше или больше параметров, которые функция должна работать, хотя это обычно заканчивается неопределенным поведением при взгляде на стандарт.
декларация printf
выглядит так:
int printf(const char*, ...);
компилятор видит только ...
, и знает, что может быть ноль или более дополнительных аргументов, которые функция может или можете не использовать. Вызываемая функция не знаю сколько аргументов он передан; он может, в лучшем случае,предположим что он прошел всю необходимую информацию и ничего больше.
сравните это с другими языками, такими как C#:
void WriteLine(string format, params object[] arguments);
здесь метод точно знает, сколько дополнительных аргументов было передано (doing arguments.Length
).
В С ++ variadic функции и особенности printf
являются частой причиной безопасности факторы уязвимости. Printf
заканчивает чтение необработанных байтов из стека, что может привести к утечке важных сведений о вашем приложении и его среде безопасности.
по этой причине Clang и GCC поддерживают специальное расширение для проверки printf
форматы. Если вы используете недопустимую строку формата, вы получите предупреждение (а не ошибку).
code.c:4:11: warning: more '%' conversions than data arguments [-Wformat]
printf("%d\n");
~~^
это просто неопределенное поведение если вы не предоставите достаточных аргументов printf
, что означает, что поведение непредсказуемо. От проект стандарта C99 раздел 7.19.6.1
функция fprintf, который также охватывает printf
для этого случая:
если недостаточно аргументов для формата, поведение не определено.
С printf
это вариативную функцию нет соответствия аргументов объявлению функции. Таким образом, компилятор должен поддерживать проверку строки формата поддержки, которая покрывается -флаг Wformat в gcc:
Проверьте вызовы printf и scanf и т. д., чтобы убедиться, что предоставленные аргументы имеют типы, соответствующие указанной строке формата, и что преобразования, указанные в строке формата, имеют смысл. Сюда входят стандартные функции и другие, заданные атрибутами format (см. раздел атрибуты функций), [...]
включение достаточного количества предупреждений компилятора важно, для этого кода gcc
С помощью -Wall
флаг говорит нам (посмотреть его в прямом эфире):
warning: format '%d' expects a matching 'int' argument [-Wformat=]
printf("%d\n");
^
это компилируется. Потому что он соответствует прототипу printf (), который является
printf(const char *,...);
во время выполнения вызова
printf("%d\n");
пытается получить значение из второго аргумента, и поскольку вы ничего не передали, он может получить некоторое значение мусора и распечатать его, поэтому поведение здесь не определено.
вы вызываете неопределенное поведение. Это ваша проблема, а не компилятора, и в основном все "разрешено".
конечно, практически каждый существующий компилятор должен иметь возможность предупредить вас об этом конкретном условии относительно printf()
, вы просто должны позволить ему (путем включения и учета предупреждений компилятора).
в общих диагностических сообщениях (вы можете думать о них как об ошибках компиляции) не гарантируется для неопределено поведение (как отсутствие достаточных аргументов для printf
вызов функции, как в вашем случае), которые не считаются нарушениями правил синтаксиса или ограничений.
C11 (N1570) §5.1.1.3 / p1 Диагностика:
соответствующая реализация должна производить по крайней мере один диагностический сообщение (идентифицировано в осуществление определенным образом), если ЕП предварительной обработки или преобразования содержит нарушение любое синтаксическое правило или ограничение, даже если поведение также явно указано как undefined или implementation-defined. Диагностические сообщения не нужно производить в других обстоятельствах.9)
другими словами, ваш компилятор может свободно переводить такой блок, но вы никогда не должны запускать его или полагаться на его поведение (поскольку оно де-факто непредсказуемо). Кроме того, вашему компилятору разрешено не предоставлять никакой документации (то есть стандарт C не применяет ее для этого), как это требуется для поведения, определенного реализацией или локали.
C11 (N1570) §3.4.3 / p2 неопределенное поведение:
Примечание возможно неопределенное поведение колеблется от игнорирования ситуации полностью с непредсказуемыми результатами, чтобы вести себя во время перевода или программа выполнение документированным образом характеристика окружающей среды (с выдачей диагностического сообщения или без нее) , завершение перевода или исполнения (с выдачей диагностическое сообщение.)
используя g++
с параметром командной строки -Wall
производит следующие диагностики:
g++ -Wall -c -g -MMD -MP -MF "build/Debug/MinGW-Windows/main.o.d" -o build/Debug/MinGW-Windows/main.o main.cpp
main.cpp: In function 'int main(void)':
main.cpp:17:16: warning: format '%d' expects a matching 'int' argument [-Wformat=]
printf("%d");
^
Это очень полезно, не так ли?
gcc/g++
также проверьте, действительно ли спецификаторы формата соответствуют типам параметров. Это действительно здорово для отладки.
по данным документация, дополнительные аргументы должны быть не менее, чем спецификаторы формата в первом аргументе. Это кажется неопределенным поведением.
какие предупреждения / сообщения Вы получили с помощью своих компиляторов? я запустил это через gcc (Ubuntu 4.8.2-19ubuntu1) и получил предупреждение
warning: format ‘%d’ expects a matching ‘int’ argument [-Wformat=]
printf("%d\n");
^
и запуск его также "вывод мусора". здесь gcc настолько умен, чтобы проанализировать выражение формата и уведомить кодер, чтобы предоставить соответствующее количество аргументов.
что я думаю произойдет: сигнатура функции printf независимая из поведения скомпилированного кода. во время компиляции, все компилятор заботится о том, чтобы проверить, есть ли хотя бы один аргумент и продолжается. однако скомпилированная функция сначала проанализирует выражение формата и, в зависимости от этого, прочитает дальнейшие аргументы из стека аргументов функций. в нем он просто ожидает соответствующих значений (int, float и т. д.) и использует их. поэтому, если вы не укажете аргумент, место в стеке вызовов функций не зарезервировано, а printf по-прежнему считывает случайную память (в этом случае в первом месте). это также объясняет вывод" мусора", который будет отличаться каждый раз, когда вы вызываете двоичный файл. вы даже можете расширить код
#include <stdio.h>
int main(void)
{
printf("%d\n%d\n");
return 0;
}
и разные фигня чисел :)
затем снова это будет зависеть от среды / процесса / компилятора, значения которого будут считываться. "неопределенное поведение" - это то, что лучше всего описывает этот эффект, иногда нулевой, иногда другой.
Я надеюсь, что это проясняет ваш вопрос!
в контексте printf () и fprintf (), согласно стандартному C11 пункту 7.21.6.1, "если для формата недостаточно аргументов, поведение не определено. Если формат исчерпан, а аргументы остаются, избыточные аргументы оцениваются (как всегда), но в противном случае игнорируются."