// File foo1.c :
#include <stdio.h> // once
void foo1(void);
void foo1(void){
puts("foo1");
}
// File foo2.c :
#include <stdio.h> // again
void foo2(void);
void foo2(void){
puts("foo2");
}
// File foomain.c :
#include <stdio.h> // yet again
void foo1(void); // again
void foo2(void); // again
int main(void){
foo1();
foo2();
puts("foomain");
return 0;
}
// create object files
gcc -fPIC foo1.c -o foo1.o // 1 stdio.h
gcc -fPIC foo2.c -o foo2.o // 1 stdio.h
// create shared library
gcc -fPIC -shared foo1.o foo2.o -o foo.so // foo.so contains stdio.h 2 times ?
// build entire program
gcc foo.so foomain.c -o foomain // foomain contains 1 stdio.h plus the 2 from foo.so ?
为什么整个程序包含3个stdio.h?似乎多余,为什么不只是1?编译器不应该只需要1吗?
对象文件包含原型是有意义的,但为什么必须在foomain.c中再次指定它们?编译器是否应该知道它们已经在foo.so中指定了?
答案 0 :(得分:6)
那是因为每个文件都是单独编译的,所以每次编译器都应该知道用于执行编译时检查的所有函数的签名。因此,每个文件都必须包含所有使用的声明,这些声明在编译文件之前由预处理器包含。
答案 1 :(得分:2)
答案 2 :(得分:1)
#include
行实际上不是编译器的一部分,而是C preprocessor。
预处理器对#include
行的作用是将文件实际包含到源中,并创建一个包含文件内容的新临时文件,其中#include
行替换为文件被包含在内。
如果你所做的只是调用函数,你根本不需要包含文件。您可能会收到有关未声明的函数的警告,但可以自行添加这些函数的原型。例如,在您的主要源文件中,您只使用puts
,而不是包含<stdio.h>
,您可以添加如下原型:
int puts(const char *s);
但是,<stdio.h>
还定义了一些结构(如FILE
结构)并声明了一些变量(如stdout
),如果你使用其中任何一个,你也需要头文件
答案 3 :(得分:0)
你可以使用@Jeff建议的包含警戒,或者只将#pragma once
放在每个标题的顶部。