C语言 C 中的头文件和源文件如何工作?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5904530/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do header and source files in C work?
提问by Dan Lugg
I've perused the possible duplicates, however none of the answers there are sinking in.
我已经仔细阅读了可能的重复项,但是没有一个答案可以解决。
tl;dr: How are source and header files related in C? Do projects sort out declaration/definition dependencies implicitly at build time?
tl;dr:源文件和头文件是如何关联的C?项目是否在构建时隐式地整理声明/定义依赖项?
I'm trying to understand how the compiler understandsthe relationship between .cand .hfiles.
我试图了解编译器如何理解.c和.h文件之间的关系。
Given these files:
鉴于这些文件:
header.h:
头文件.h:
int returnSeven(void);
source.c:
来源.c:
int returnSeven(void){
return 7;
}
main.c:
主文件:
#include <stdio.h>
#include <stdlib.h>
#include "header.h"
int main(void){
printf("%d", returnSeven());
return 0;
}
Will this mess compile? I'm currently doing my work in NetBeans 7.0with gccfrom Cygwin which automates much of the build task. When a project is compiled will the project files involved sort out this implicit inclusion of source.cbased on the declarations in header.h?
这个烂摊子会编译吗?我目前正在NetBeans 7.0 中使用Cygwin 的gcc进行工作,它可以自动执行大部分构建任务。编译项目时,所涉及的项目文件是否会source.c根据header.h?
回答by Jesper
Converting C source code files to an executable program is normally done in two steps: compilingand linking.
将 C 源代码文件转换为可执行程序通常分两步完成:编译和链接。
First, the compiler converts the source code to object files (*.o). Then, the linker takes these object files, together with statically-linked libraries and creates an executable program.
首先,编译器将源代码转换为目标文件 ( *.o)。然后,链接器将这些目标文件连同静态链接库一起创建一个可执行程序。
In the first step, the compiler takes a compilation unit, which is normally a preprocessed source file (so, a source file with the contents of all the headers that it #includes) and converts that to an object file.
在第一步中,编译器采用一个编译单元,它通常是一个预处理过的源文件(因此,一个包含它所有头文件内容的源文件#include)并将其转换为目标文件。
In each compilation unit, all the functions that are used must be declared, to let the compiler know that the function exists and what its arguments are. In your example, the declaration of the function returnSevenis in the header file header.h. When you compile main.c, you include the header with the declaration so that the compiler knows that returnSevenexists when it compiles main.c.
在每个编译单元中,必须声明所有使用的函数,让编译器知道该函数存在以及它的参数是什么。在您的示例中,函数的声明returnSeven位于头文件中header.h。编译时main.c,在声明中包含头文件,以便编译器在编译时知道它returnSeven存在main.c。
When the linker does its job, it needs to find the definitionof each function. Each function has to be defined exactly once in one of the object files - if there are multiple object files that contain the definition of the same function, the linker will stop with an error.
当链接器完成它的工作时,它需要找到每个函数的定义。每个函数必须在其中一个目标文件中准确定义一次 - 如果有多个目标文件包含相同函数的定义,链接器将因错误而停止。
Your function returnSevenis defined in source.c(and the mainfunction is defined in main.c).
您的函数returnSeven定义于source.c(并且main函数定义于main.c)。
So, to summarize, you have two compilation units: source.cand main.c(with the header files that it includes). You compile these to two object files: source.oand main.o. The first one will contain the definition of returnSeven, the second one the definition of main. Then the linker will glue those two together in an executable program.
因此,总而言之,您有两个编译单元:source.c和main.c(以及它包含的头文件)。您将它们编译为两个目标文件:source.o和main.o. 第一个将包含 的定义returnSeven,第二个将包含 的定义main。然后链接器会将这两者粘合在一个可执行程序中。
About linkage:
关于联动:
There is external linkageand internal linkage. By default, functions have external linkage, which means that the compiler makes these functions visible to the linker. If you make a function static, it has internal linkage - it is only visible inside the compilation unit in which it is defined (the linker won't know that it exists). This can be useful for functions that do something internally in a source file and that you want to hide from the rest of the program.
有外部联动和内部联动。默认情况下,函数具有外部链接,这意味着编译器使这些函数对链接器可见。如果你创建一个函数static,它有内部链接——它只在定义它的编译单元内可见(链接器不会知道它存在)。这对于在源文件内部执行某些操作并且您希望对程序的其余部分隐藏的函数很有用。
回答by Oliver Charlesworth
The C language has no concept of source files and header files (and neither does the compiler). This is merely a convention; remember that a header file is always #included into a source file; the preprocessor literally just copy-pastes the contents, before proper compilation begins.
C 语言没有源文件和头文件的概念(编译器也没有)。这只是一个约定;请记住,头文件总是#included 到源文件中;在正确的编译开始之前,预处理器实际上只是复制粘贴内容。
Your example shouldcompile (foolish syntax errors notwithstanding). Using GCC, for example, you might first do:
您的示例应该可以编译(尽管存在愚蠢的语法错误)。例如,使用 GCC,您可能首先执行以下操作:
gcc -c -o source.o source.c
gcc -c -o main.o main.c
This compiles each source file separately, creating independent object files. At this stage, returnSeven()has not been resolved inside main.c; the compiler has merely marked the object file in a way that states that it must be resolved in the future. So at this stage, it's not a problem that main.ccan't see a definitionof returnSeven(). (Note: this is distinct from the fact that main.cmust be able to see a declarationof returnSeven()in order to compile; it must know that it is indeed a function, and what its prototype is. That is why you must #include "source.h"in main.c.)
这会分别编译每个源文件,创建独立的目标文件。现阶段,returnSeven()里面还没有解决main.c;编译器只是以某种方式标记了目标文件,表明将来必须解决它。所以在这个阶段,main.c看不到定义的问题不是问题returnSeven()。(注:这是一个事实,即不同的main.c必须能够看到申报的returnSeven(),以编译,它必须知道,这的确是一个功能,以及它的原型是这就是为什么你必须这样做。#include "source.h"在main.c。)
You then do:
然后你做:
gcc -o my_prog source.o main.o
This linksthe two object files together into an executable binary, and performs resolution of symbols. In our example, this is possible, because main.orequires returnSeven(), and this is exposed by source.o. In cases where everything doesn't match up, a linker error would result.
这将两个目标文件链接到一个可执行的二进制文件中,并执行符号解析。在我们的示例中,这是可能的,因为main.orequires returnSeven(),并且 this 由 公开source.o。如果所有内容都不匹配,则会导致链接器错误。
回答by pmg
There is nothing magic about compilation. Nor automatic!
编译没有什么神奇之处。也不自动!
Header files basically provide information to the compiler, almost never code.
That information alone, is usually not enough to create a full program.
头文件基本上向编译器提供信息,几乎从不提供代码。
仅凭这些信息通常不足以创建完整的程序。
Consider the "hello world" program (with the simpler putsfunction):
考虑“hello world”程序(具有更简单的puts功能):
#include <stdio.h>
int main(void) {
puts("Hello, World!");
return 0;
}
without the header, the compiler does not know how to deal with puts()(it is not a C keyword). The header lets the compiler know how to manage the arguments and return value.
没有头文件,编译器不知道如何处理puts()(它不是 C 关键字)。头文件让编译器知道如何管理参数和返回值。
How the function works, however, is not specified anywhere in this simple code. Somebody else has written the code for puts()and included the compiled code in a library. The code in that library is included with the compiled code for your source as part of the compilation process.
然而,在这个简单的代码中没有指定函数的工作原理。其他人编写了代码puts()并将编译后的代码包含在库中。作为编译过程的一部分,该库中的代码包含在源代码的编译代码中。
Now consider you wanted your own version of puts()
现在考虑你想要你自己的版本 puts()
int main(void) {
myputs("Hello, World!");
return 0;
}
Compiling just this code gives an error because the compiler has no information about the function. You can provide that information
仅编译此代码会产生错误,因为编译器没有关于该函数的信息。你可以提供这些信息
int myputs(const char *line);
int main(void) {
myputs("Hello, World!");
return 0;
}
and the code now compiles --- but does not link, ie does not produce an executable, because there is no code for myputs(). So you write the code for myputs()in a file called "myputs.c"
并且代码现在可以编译 --- 但不链接,即不生成可执行文件,因为没有myputs(). 所以你myputs()在一个名为“myputs.c”的文件中编写代码
#include <stdio.h>
int myputs(const char *line) {
while (*line) putchar(*line++);
return 0;
}
and you have to remember to compile bothyour first source file and "myputs.c" together.
你必须记住编译双方的第一个源文件和“myputs.c”在一起。
After a while your "myputs.c" file has expanded to a hand full of functions and you need to include the information about all the functions (their prototypes) in the source files that want to use them.
It is more convenient to write all the prototypes in a single file and #includethat file. With the inclusion you run no risk of making a mistake when typing the prototype.
一段时间后,您的“myputs.c”文件已扩展为一手完整的函数,您需要在要使用它们的源文件中包含有关所有函数(它们的原型)的信息。
将所有原型写在一个文件和#include那个文件中会更方便。包含在内,您在输入原型时不会有犯错的风险。
You still have to compile and link all the code files together though.
尽管如此,您仍然必须编译和链接所有代码文件。
When they grow even more, you put all the already compiled code in a library ... and that's another story :)
当它们增长得更多时,您将所有已编译的代码放在一个库中……这是另一回事:)
回答by Hack Saw
Header files are used to separate the interface declarations which correspond to the implementations in the source files. They're abused in other ways, but this is the common case. This isn't for the compiler, it's for the humans writing the code.
头文件用于分隔与源文件中的实现相对应的接口声明。他们以其他方式被滥用,但这是常见的情况。这不是针对编译器,而是针对编写代码的人。
Most compilers don't actually see the two files separately, they are combined by the preprocessor.
大多数编译器实际上并没有分别查看这两个文件,它们由预处理器组合而成。
回答by Mark Wilkins
The compiler itself has no specific "knowledge" of relationships between source files and header files. Those types of relationships are typically defined by project files (e.g., makefile, solution, etc.).
编译器本身没有关于源文件和头文件之间关系的特定“知识”。这些类型的关系通常由项目文件(例如,makefile、解决方案等)定义。
The given example appears as if it would compile correctly. You would need to compile both source files and then the linker would need both object files to produce the executable.
给定的示例看起来好像可以正确编译。您需要编译两个源文件,然后链接器需要两个目标文件来生成可执行文件。

