Почему #include <stdio.h> не требуется для использования printf ()? - PullRequest
29 голосов
/ 03 декабря 2008

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

>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

Ответы [ 3 ]

37 голосов
/ 03 декабря 2008

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

int printf();

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

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

31 голосов
/ 04 декабря 2008

В режиме строгого соответствия (что означает «в теории») вы вызываете неопределенное поведение (что плохо), когда вы вызываете функцию, которая принимает переменное число аргументов без объявления прототипа функции в области видимости. Это означает, что компилятору разрешено делать все, что ему нравится, с программой, которая использует printf() без прототипа из #include <stdio.h> или эквивалентного объявления. «Все что угодно» включает в себя правильную работу в качестве одного из вариантов; похоже, это вариант, выбранный вашим примером.

На практике код будет работать нормально с большинством практических компиляторов даже без формального объявления функции printf().

Как указывал qrdl, функция была найдена, потому что компилятор C связывается с библиотекой C.

Обратите внимание, что комментарий Криса Янга о C99 и 'implicit int' точен, но правило о том, что 'функции переменных аргументов должны иметь прототип в области видимости', применимо как к C89, так и к C99. Большинство компиляторов не работают в строгом режиме совместимости с C99 по умолчанию, потому что слишком много кода, который не скомпилируется таким образом.

Крис Янг прокомментировал:

Для пояснения, мой комментарий был о том, что C99 удаляет неявные объявления. Говоря «implicit 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() были определены с помощью многоточия, этот прототип потребовался бы в теории !)

9 голосов
/ 03 декабря 2008

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

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

...