Почему #include <stdio.h> не требуется использовать printf()?
-
22-07-2019 - |
Вопрос
Стенограмма заседания:
>type lookma.c
int main() {
printf("%s", "no stdio.h");
}
>cl lookma.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.762 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
lookma.c
Microsoft (R) Incremental Linker Version 8.00.50727.762
Copyright (C) Microsoft Corporation. All rights reserved.
/out:lookma.exe
lookma.obj
>lookma
no stdio.h
Решение
В режиме строгого соответствия (что означает "теоретически") вы вызываете неопределенное поведение (что плохо) при вызове функции, которая принимает переменное количество аргументов без объявления прототипа функции в области видимости.Это означает, что компилятору разрешено делать все, что ему заблагорассудится, с программой, которая использует printf()
без прототипа из #include <stdio.h>
или эквивалентное заявление."Все, что ему нравится" включает корректную работу в качестве одного из вариантов;похоже, это вариант, выбранный в вашем примере.
На практике код будет нормально работать с большинством практичных компиляторов даже без формального объявления printf()
функция.
Как было указано qrdl, функция была найдена, потому что компилятор C связывается с библиотекой C.
Обратите внимание, что комментарий Криса Янга о C99 и "неявном int" точен, но правило о том, что "функции с переменными аргументами должны иметь прототип в области видимости", применимо как к C89, так и к C99.Большинство компиляторов по умолчанию не работают в режиме строгой совместимости с C99, потому что существует слишком много кода, который не компилировался бы подобным образом.
Крис Янг прокомментировал:
Чтобы внести ясность, мой комментарий касался удаления неявных объявлений C99.Говоря "неявный int", я думаю, вы имеете в виду функцию C89, разрешающую такие объявления, как foo(void);чтобы означать int foo(void);, что-то C99 также удалено.
Крис, конечно, прав.Из стандарта C99 были удалены две функции "неявного объявления".В предисловии к стандарту они перечислены следующим образом:
- удалить неявный
int
- удалить объявление неявной функции
Я недостаточно ясно мыслил (и, следовательно, писал).Тем не менее, как C89, так и C99 требуют прототипа в области видимости для функций, которые принимают переменное количество аргументов.
Чтобы проиллюстрировать:
extern int pqr();
int main(void)
{
int i = pqr(1, 3);
return i;
}
Без первой строки это правильный фрагмент C89 с неявным объявлением функции pqr()
как функция, которая возвращает целое число (с неуказанными аргументами).Если первая строка заменена на extern pqr();
, тогда это правильный фрагмент C89 с явным объявлением pqr()
как функция, которая возвращает целое число (с неопределенными аргументами), но возвращаемый тип 'неявный int
'.Как написано, функция явно объявлена и имеет явное int
возвращаемый тип - но он по-прежнему имеет неопределенные аргументы.Я считаю, что это допустимо C99 - хотя и не совсем желательно.Конечно, GCC (3.4.4) принимает это вместе с параметрами '-std=c99 -pedantic
".В идеале объявление функции должно включать полный прототип.(И, если pqr()
были бы определены с многоточием, потребовался бы этот прототип теоретически!)
Другие советы
Вы изначально пометили этот C ++, но, похоже, это программа на C. C автоматически предоставит неявное объявление для функции, если в области нет прототипа (например, из-за пропуска #include < stdio.h >). Неявное объявление будет следующим:
int printf();
Это означает, что printf - это функция, которая возвращает int и может принимать любое количество аргументов. Этот прототип сработал для вашего звонка. Вы должны #include < stdio.h >
Наконец, я должен добавить, что текущий стандарт C (ISO / IEC 9899: 1999 или разговорный «C99») не разрешает неявные объявления, и эта программа не будет соответствовать. Неявные объявления были удалены. Я считаю, что ваш компилятор не поддерживает C99. C ++ также требует правильных прототипов и не делает неявных объявлений. Р>
printf ()
находится в стандартной библиотеке C, и компоновщик всегда связывает стандартную библиотеку с вашим исполняемым файлом, поэтому любые стандартные функции будут найдены и проблем с компоновкой не будет.
Отказ от включения соответствующего заголовка приводит к использованию функции, которая не является прототипом, что может привести к проблемам, поскольку компилятор C предполагает, что функция без прототипа возвращает int
и принимает переменное число аргументов. Поэтому всегда включайте заголовок - это ваш защитный забор.