Почему #include ‹stdio.h› не требуется для использования printf()?

Стенограмма сеанса:

> 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

person Constantin    schedule 03.12.2008    source источник
comment
Обратите внимание, что в C89/C90 вы должны предоставить возвращаемое значение из main(). C99 позволяет вам опускать return 0; или его эквивалент в конце main().   -  person Jonathan Leffler    schedule 30.10.2015
comment
В каком контексте? Окно командной строки в Windows?   -  person Peter Mortensen    schedule 26.04.2021


Ответы (3)


В режиме строгого соответствия (что означает «теоретически») вы вызываете неопределенное поведение (что плохо), когда вызываете функцию, которая принимает переменное количество аргументов без объявления прототипа функции в области видимости. Это означает, что компилятору разрешено делать с программой, использующей 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() было определено с многоточием, этот прототип потребовался бы теоретически!)

person Jonathan Leffler    schedule 04.12.2008
comment
Чтобы уточнить, мой комментарий был о том, что C99 удаляет неявные описания. Говоря неявный int, я думаю, вы имеете в виду функцию C89, разрешающую такие объявления, как foo(void); означать int foo(void); что-то C99 также удалил. - person Chris Young; 05.12.2008

Изначально вы пометили эту программу на языке C++, но она оказалась программой на языке C. C автоматически предоставит неявное объявление функции, если в области видимости нет прототипа (например, из-за пропуска #include ‹stdio.h›). Неявное объявление будет:

int printf();

Это означает, что printf — это функция, которая возвращает целое число и может принимать любое количество аргументов. Этот прототип сработал для вашего звонка. Вы должны #include ‹stdio.h›

Наконец, я должен добавить, что текущий стандарт C (ISO/IEC 9899:1999 или в просторечии "C99") не разрешает неявные объявления, и эта программа не соответствует ему. Неявные объявления были удалены. Я считаю, что ваш компилятор не поддерживает C99. C++ также требует правильных прототипов и не использует неявные объявления.

person Chris Young    schedule 03.12.2008

printf() находится в стандартной библиотеке C, а компоновщик всегда связывает стандартную библиотеку с вашим исполняемым файлом, поэтому любые стандартные функции будут найдены и проблем с компоновкой не возникнет.

Отсутствие соответствующего заголовка приводит к использованию функции без прототипа, что может привести к проблемам, поскольку компилятор C предполагает, что функция без прототипа возвращает int и принимает переменное количество аргументов. Поэтому всегда включайте заголовок — это ваша защитная ограда.

person qrdl    schedule 03.12.2008
comment
Не «переменное количество аргументов», а неопределенное, но фиксированное количество аргументов. - person Jonathan Leffler; 04.12.2008