C программа без заголовка - PullRequest
3 голосов
/ 19 января 2012

Я пишу программу "Здравствуй, мир" на языке C.

void main()
{ printf("Hello World"); }
// note that I haven't included any header file

Программа компилируется с предупреждением как

vikram@vikram-Studio-XPS-1645:~$ gcc hello.c 
hello.c: In function ‘main’:
hello.c:2:2: warning: incompatible implicit declaration of built-in function ‘printf’
vikram@vikram-Studio-XPS-1645:~$ ./a.out 
Hello Worldvikram@vikram-Studio-XPS-1645:~$

Как это возможно? Как ОС связывает библиотеку, не включая заголовки?

Ответы [ 5 ]

7 голосов
/ 19 января 2012

Функция printf находится в библиотеке C (libc в вашем случае), которая неявно связана (на самом деле gcc имеет встроенную функцию printf, но это не относится к делу).

Включение заголовка не дает никаких функций для компоновщика, оно просто сообщает компилятору об их объявлениях (т. Е. " как они выглядят ").

Очевидно, что вы всегда должны включать заголовки, иначе вы заставите компилятор делать предположения о том, как будут выглядеть функции.

6 голосов
/ 19 января 2012

Компилятор создает ваш исходный файл со ссылкой на функцию с именем printf(), без , зная, какие аргументы он на самом деле принимает или каков его тип возвращаемого значения. Сгенерированная сборка содержит push адреса строки "Hello World" в области статических данных вашей программы, за которой следуют от call до printf.

При связывании вашего объектного файла с исполняемым файлом компоновщик видит ссылку на printf и предоставляет функцию стандартной библиотеки C printf(). По совпадению переданный вами аргумент (const char*) совместим с объявлением действительного printf(), поэтому он функционирует правильно. Однако обратите внимание, что printf(), который ваша программа неявно объявляет, имеет тип возврата int (я думаю), который также имеет стандарт printf(); но если бы они отличались, и вы должны были присвоить результат вызова printf() переменной, вы оказались бы в стране неопределенного поведения и, скорее всего, получили бы неправильное значение.

Короче говоря: #include правильные заголовки, чтобы получить правильные объявления для функций, которые вы используете, потому что этот вид неявного объявления устарел, потому что он подвержен ошибкам.

1 голос
/ 19 января 2012

Заголовок обычно 1 содержит только объявления функций, символические константы и определения макросов;обычно оно не включает определения функций.

Все stdio.h дает вам объявление прототипа для printf:

int printf(const char * restrict format, ...); // as of C99

Реализация из printf находится в отдельном файле библиотеки, которыйкод ссылки против.

Ваш код "работает" по двум причинам:

  1. В C89 и более ранних версиях, если компилятор видит вызов функции перед объявлением или определением этой функции, онбудет предполагать, что функция возвращает int и принимает неопределенное количество параметров;

  2. Реализация printf возвращает int, и вы передали аргумент, который просто происходитчтобы быть совместимым с тем, что реализация printf ожидает для первого аргумента.

И чтобы повторить то, что говорят все остальные, используйте int main(void) или int main(int argc, char **argv);если документация вашего компилятора явно не перечисляет void main() в качестве допустимой подписи, то при ее использовании будет вызываться неопределенное поведение (что означает все, начиная с выполнения вашего кода без видимых проблем, до сбоя при выходе и до полной загрузки).


  1. Я говорю "обычно";Я столкнулся с некоторыми заголовками, которые содержали код, но обычно они написаны людьми, которые не знали, что они делают.В очень редких случаях размещение кода в заголовке оправдано, но, как правило, это плохая практика.
1 голос
/ 19 января 2012

В C, если вы используете стандартную библиотечную функцию, вам необходимо включить стандартный заголовок, в котором объявлена ​​функция. Для printf необходимо включить заголовочный файл stdio.h.

В C89 (и GNU C89, который является языком по умолчанию для gcc), объявление функции иногда может быть опущено, потому что есть функция, называемая неявным объявлением функции: когда используется идентификатор функции foo и функция не было объявлено, реализация будет использовать это объявление:

 /* foo is a function with an unspecified number of arguments */
extern int foo();

Но это объявление приемлемо только для функций, которые возвращают int с неопределенным, но фиксированным числом аргументов. Если функция принимает переменное число аргументов (например, printf), такая программа вызовет неопределенное поведение.

Вот что говорит C89 / C90:

(C90, 6.7.1) "Если функция, которая принимает переменное число аргументов, определена без списка типов параметров, который заканчивается многоточием, поведение не определено.

Так что gcc достаточно любезен для компиляции даже в C89 и GNU C89: компилятор может отказаться от компиляции.

Также обратите внимание, что

void main() { ... }

не является допустимым определением для main (по крайней мере, для размещенных реализаций , что, вероятно, соответствует вашему случаю).

Если ваша основная функция не принимает аргументов, используйте это правильное определение:

int main(void) { ... }
0 голосов
/ 19 января 2012

hello.c: 2: 2: предупреждение: несовместимое неявное объявление встроенной функции «printf»

Чтобы справиться с этим предупреждением, вы должны включить заголовочный файл (stdio.h). Вы случайно используете старую функцию C, которая устарела с 1999 года.

Кроме того, тот факт, что ссылка не перестает работать, просто означает, что стандартная библиотека C связана по умолчанию. Независимо от того, включили вы соответствующий заголовок или нет, несущественно.

...