Это на самом деле хорошая практика, поскольку компилятор может проверить ваше объявление (в заголовке) на соответствие вашему определению (в hello_fn.c
).
Без этого невозможно обеспечить их соответствие.Вы можете довольно легко поместить:
void hello (int notCorrect) { ... }
в ваш исходный файл C, и он с радостью скомпилирует два исходных файла независимо, а затем (в зависимости от того, что вы на самом деле делаете в бите ...
) эффектно завершится неудачно при запуске-time.
Например, рассмотрим файлы:
hello.c:
#include <stdio.h>
#include "hello.h"
int main(void) { hello(42); }
hello_fn.c:
#include <stdio.h>
//#include "hello.h"
void hello(char *x) { puts(x); }
hello.h:
int hello(int x);
Это прекрасно компилируется, поскольку каждый исходный файл C внутренне согласован в том, что он утверждает.Пара hello.c/hello.h
считает, что hello
принимает int
, а hello_fn.c
думает, что она принимает строку C.
Я получаю дамп ядра (1) во время работы, потому чтозначение 42
не является допустимым строковым адресом.Когда я раскомментирую строку include
в hello_fn.c
, компилятор жалуется (справедливо), что мое объявление и определение не совпадают (потому что пара hello_fn.c/hello.h
теперь несовместима).
Кроме того, может быть другой материал в заголовке (но не в вашем примере), который существует только в заголовке.Обычно это объявления, которые являются общими для вызывающего и вызываемого абонентов, такие как типы, extern
items, #define
items и т. Д.Не имеет смысла объявлять их отдельно в заголовке и исходном файле.
(1) Фактические результаты могут отличаться, посколькуэто неопределенное поведение.