3
// 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 ?
  1. 为什么整个程序包含 3 个 stdio.h ?似乎多余,为什么不只是 1 ?编译器不应该只需要 1 吗?

  2. 目标文件包含原型是有意义的,但为什么必须在 foomain.c 中再次指定它们?编译器不应该知道它们已经在 foo.so 中指定了吗?

4

4 回答 4

8

那是因为每个文件都是单独编译的,所以每次编译器都应该知道用于执行编译时检查的所有函数的签名。因此,每个文件都必须包含所有使用的声明,这些声明在文件编译之前由预处理器包含。

于 2012-04-23T10:10:54.613 回答
3

如果您查看大多数头文件的顶部,它们都有一个包含保护来阻止双重包含。

#ifndef FOO
#define FOO

#endif

有关详细信息,请参阅包含防护

于 2012-04-23T10:08:28.097 回答
2

这些#include行实际上不是编译器的一部分,而是C 预处理器

预处理器对#include行所做的是将文件实际包含到源代码中,并创建一个包含文件内容的新临时文件,其中#include行被包含文件的内容替换。

如果您所做的只是调用函数,那么您实际上根本不需要包含文件。您可能会收到有关未声明函数的警告,但这些警告可能是您自己为这些函数添加原型。例如,在您的主源文件中,您只使用puts,而不是包含<stdio.h>您可以添加这样的原型:

int puts(const char *s);

但是,<stdio.h>还定义了一些结构(如FILE结构)并声明了一些变量(如stdout),如果您使用其中任何一个,则还需要头文件。

于 2012-04-23T10:09:52.027 回答
0

您可以按照@Jeff 的建议使用包含防护,或者只是放在#pragma once每个标题的顶部。

于 2012-04-23T10:09:35.870 回答