C/C++ 头文件和实现文件:它们是如何工作的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9224537/
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
C/C++ header and implementation files: How do they work?
提问by nickelpro
This is probablya stupid question, but I've searched for quite a while now here and on the web and couldn't come up with a clear answer (did my due diligence googling).
这可能是一个愚蠢的问题,但我已经在这里和网络上搜索了很长时间,但找不到明确的答案(我的尽职调查谷歌搜索)。
So I'm new to programming... My question is, how does the main function know about function definitions (implementations) in a different file?
所以我是编程新手......我的问题是,主函数如何知道不同文件中的函数定义(实现)?
ex. Say I have 3 files
前任。说我有 3 个文件
- main.cpp
- myfunction.cpp
- myfunction.hpp
- 主程序
- 我的函数.cpp
- 我的函数.hpp
//main.cpp
#include "myfunction.hpp"
int main() {
int A = myfunction( 12 );
...
}
-
——
//myfunction.cpp
#include "myfunction.hpp"
int myfunction( int x ) {
return x * x;
}
-
——
//myfunction.hpp
int myfunction( int x );
-
——
I get how the preprocessor includes the header code, but how do the header and main function even know the function definition exists, much less utilize it?
我知道预处理器如何包含头代码,但是头和主函数如何知道函数定义存在,更不用说使用它了?
I apologize if this isn't clear or I'm vastly mistaken about something, new here
如果这不清楚或者我对某些事情有很大的误解,我很抱歉,这里是新的
回答by Ed Heal
The header file declaresfunctions/classes - i.e. tells the compiler when it is compiling a .cpp
file what functions/classes are available.
头文件声明函数/类——即在编译.cpp
文件时告诉编译器哪些函数/类可用。
The .cpp
file defines those functions - i.e. the compiler compiles the code and therefore produces the actual machine code to perform those actions that are declared in the corresponding .hpp
file.
该.cpp
文件定义了这些函数 - 即编译器编译代码并因此生成实际的机器代码来执行在相应.hpp
文件中声明的那些操作。
In your example, main.cpp
includes a .hpp
file. The preprocessor replaces the #include
with the contents of the .hpp
file. This file tells the compiler that the function myfunction
is defined elsewhere and it takes one parameter (an int
) and returns an int
.
在您的示例中,main.cpp
包含一个.hpp
文件。预处理器用文件#include
的内容替换.hpp
。该文件告诉编译器该函数myfunction
是在别处定义的,它接受一个参数 (an int
) 并返回一个int
.
So when you compile main.cpp
into object file (.o extension) it makes a note in that file that it requires the function myfunction
. When you compile myfunction.cpp
into an object file, the object file has a note in it that it has the definition for myfunction
.
因此,当您编译main.cpp
为目标文件(.o 扩展名)时,它会在该文件中注明它需要函数myfunction
. 当您编译myfunction.cpp
为目标文件时,目标文件中有一个注释,说明它具有myfunction
.
Then when you come to linking the two object files together into an executable, the linker ties the ends up - i.e. main.o
uses myfunction
as defined in myfunction.o
.
然后,当你连接两个目标文件来组合成一个可执行,链接关系的结束了-即main.o
应用myfunction
中定义myfunction.o
。
I hope that helps
我希望有帮助
回答by Coren
You have to understand that compilation is a 2-steps operations, from a user point of view.
您必须了解,从用户的角度来看,编译是一个 2 步操作。
1st Step : Object compilation
第一步:对象编译
During this step, your *.c files are individuallycompiled into separateobject files. It means that when main.cppis compiled, it doesn't know anything about your myfunction.cpp. The only thing that he knows is that you declarethat a function with this signature : int myfunction( int x )
exists in an other object file.
在此步骤中,您的 *.c 文件被单独编译为单独的目标文件。这意味着编译main.cpp时,它对myfunction.cpp 一无所知。他唯一知道的是你声明了一个带有这个签名的函数:int myfunction( int x )
存在于另一个目标文件中。
Compiler will keep a reference of this call and include it directly in the object file. Object file will contain a "I have to call myfunctionwith an intand it will return to me with an int. It keeps an index of all externcalls in order to be able to link with other afterwards.
编译器将保留此调用的引用并将其直接包含在目标文件中。对象文件将包含一个“我要叫myfunction的与一个int,它会回到我一个int,它保留了所有的索引的extern电话,以便能够链接与其他算账。
2nd Step : Linking
第二步:链接
During this step, the linkerwill take a look at all those indexes of your object files and will try to solve dependencies within those files. If one is not there, you'll get the famous undefined symbol XXX
from it. He will then translate those references into real memory address in a result file : either a binary or a library.
在此步骤中,链接器将查看目标文件的所有索引,并尝试解决这些文件中的依赖关系。如果一个人不在那里,你就会因此而出名undefined symbol XXX
。然后他会将这些引用转换为结果文件中的实际内存地址:二进制文件或库文件。
And then, you can begin to ask how is this possible to do that with gigantic program like an Office Suite, which have tons of methods & objects ? Well, they use the shared librarymechanism. You know them with your '.dll' and/or '.so' files you have on your Unix/Windows workstation. It allows to postpone solving of undefined symbol until the program is run.
然后,您可以开始问如何使用像 Office 套件这样具有大量方法和对象的巨大程序来做到这一点?嗯,他们使用共享库机制。您可以通过 Unix/Windows 工作站上的“.dll”和/或“.so”文件了解它们。它允许将未定义符号的求解推迟到程序运行。
It even allows to solve undefined symbol on demand, with dl*functions.
它甚至允许使用dl*函数按需解决未定义的符号。
回答by Matthieu M.
1. The principle
一、原理
When you write:
当你写:
int A = myfunction(12);
This is translated to:
这被翻译成:
int A = @call(myfunction, 12);
where @call
can be seen as a dictionary look-up. And if you think about the dictionary analogy, you can certainly know about a word (smogashboard?) before knowing its definition. All you need is that, at runtime, the definition be in the dictionary.
where@call
可以看作是一个字典查找。如果您考虑字典的类比,您当然可以在了解一个词的定义之前就了解它(smogashboard?)。您所需要的只是,在运行时,定义在字典中。
2. A point on ABI
2. 关于 ABI 的一点
How does this @callwork ? Because of the ABI. The ABI is a way that describes many things, and among those how to perform a call to a given function (depending on its parameters). The call contract is simple: it simply says where each of the function arguments can be found (some will be in the processor's registers, some others on the stack).
这个@call 是如何工作的?因为 ABI。ABI 是一种描述许多事物的方式,其中包括如何执行对给定函数的调用(取决于其参数)。调用契约很简单:它只是说明可以找到每个函数参数的位置(有些在处理器的寄存器中,有些在堆栈中)。
Therefore, @call actually does:
因此,@call 实际上是:
@push 12, reg0
@invoke myfunction
And the function definition knows that its first argument (x) is located in reg0
.
并且函数定义知道它的第一个参数 ( x) 位于reg0
.
3. But I though dictionaries were for dynamic languages ?
3. 但我认为字典是针对动态语言的?
And you are right, to an extent. Dynamic languages are typically implemented with a hash table for symbol lookup that is dynamically populated.
在某种程度上,你是对的。动态语言通常使用哈希表来实现,用于动态填充的符号查找。
For C++, the compiler will transform a translation unit (roughly speaking, a preprocessed source file) into an object (.o
or .obj
in general). Each object contains a table of the symbols it references but for which the definition is not known:
对于 C++,编译器会将一个翻译单元(粗略地说,一个预处理的源文件)转换为一个对象(.o
或.obj
一般情况下)。每个对象都包含一个它所引用的符号表,但其定义未知:
.undefined
[0]: myfunction
Then the linker will bring together the objects and reconciliate the symbols. There are two kinds of symbols at this point:
然后链接器会将对象组合在一起并协调符号。此时有两种符号:
- those which are within the library, and can be referenced through an offset (the final address is still unknown)
- those which are outside the library, and whose address is completely unknown until runtime.
- 那些在库中的,可以通过偏移量引用的(最终地址仍然未知)
- 那些在库之外,并且直到运行时其地址是完全未知的。
Both can be treated in the same fashion.
两者都可以以相同的方式处理。
.dynamic
[0]: myfunction at <undefined-address>
And then the code will reference the look-up entry:
然后代码将引用查找条目:
@invoke .dynamic[0]
When the library is loaded (DLL_Open
for example), the runtime will finally know wherethe symbol is mapped in memory, and overwrite the <undefined-address>
with the real address (for this run).
当库被加载(DLL_Open
例如),运行时会终于知道其中的符号映射到存储器中,并覆盖<undefined-address>
与真实地址(这个运行)。
回答by yves Baumes
As suggested in Matthieu M.'s comment, it is the linker jobto find the right "function" at the right place. Compilation steps are, roughly:
正如 Matthieu M. 的评论中所建议的,在正确的位置找到正确的“函数”是链接器的工作。编译步骤大致如下:
- The compiler is invoked for each cpp file and translate it to an object file (binary code) with a symbol tablewhich associates function name (names are mangled in c++) to their location in the object file.
- The linker is invoked only one time: whith every object file in parameter. It will resolve function call location from one object file to another thanks to symbol tables. One main() function MUST exist somewhere. Eventually a binary executable file is produced when the linker found everything it needs.
- 为每个 cpp 文件调用编译器,并将其转换为带有符号表的目标文件(二进制代码),该符号表将函数名称(在 C++ 中名称被修改)与其在目标文件中的位置相关联。
- 链接器只调用一次:参数中的每个目标文件。由于符号表,它会将函数调用位置从一个目标文件解析到另一个目标文件。一个 main() 函数必须存在于某处。最终,当链接器找到它需要的所有东西时,就会生成一个二进制可执行文件。
回答by Ram
The preprocessor includes the content of the header files in to the cpp files (cpp files are called translation unit). When you compile the code, each translational unit separately is checked for semantic and syntactic errors. The presence of function definitions across translation units is not considered. .obj files are generated after compilation.
预处理器将头文件的内容包含在 cpp 文件中(cpp 文件称为翻译单元)。编译代码时,会分别检查每个翻译单元的语义和句法错误。不考虑跨翻译单元的函数定义的存在。.obj 文件在编译后生成。
In the next step when the obj files are linked. the definition of functions (member functions for classes) that are used gets searched and linking happens. If the function is not found a linker error is thrown.
在下一步链接 obj 文件时。使用的函数(类的成员函数)的定义被搜索并发生链接。如果未找到该函数,则会引发链接器错误。
In your example, If the function was not defined in myfunction.cpp, compilation would still go on with no problem. An error would be reported in the linking step.
在你的例子中,如果函数没有在 myfunction.cpp 中定义,编译仍然会继续没有问题。链接步骤中将报告错误。
回答by LihO
int myfunction(int);
is the function prototype. You declare function with it so that compiler knows that you are calling this function when you write myfunction(0);
.
int myfunction(int);
是函数原型。你用它声明函数,以便编译器知道你在编写时调用了这个函数myfunction(0);
。
And how do the header and main function even know the function definition exists?
Well, this is the job of Linker.
而如何标题和主要功能甚至不知道该函数的定义存在?
嗯,这是Linker的工作。
回答by atoMerz
When you compile a program, the preprocessor adds source code of each header file to the file that included it. The compiler compiles EVERY.cpp
file. The result is a number of .obj
files.
After that comes the linker. Linker takes all .obj
files, starting from you main file, Whenever it finds a reference that has no definition (e.g. a variable, function or class) it tries to locate the respective definition in other .obj
files created at compile stage or supplied to linker at the beginning of linking stage.
Now to answer your question: each .cpp
file is compile into a .obj
file containing instructions in machine code. When you include a .hpp
file and use some function that's defined in another .cpp
file, at linking stage the linker looks for that function definition in the respective .obj
file. That's how it finds it.
编译程序时,预处理器会将每个头文件的源代码添加到包含它的文件中。编译器编译每个.cpp
文件。结果是多个.obj
文件。
之后是链接器。链接器获取所有.obj
文件,从您的主文件开始,每当它找到没有定义的引用(例如变量、函数或类)时,它会尝试.obj
在编译阶段创建的其他文件中找到相应的定义,或者在开始时提供给链接器的连接阶段。
现在回答你的问题:每个.cpp
文件都被编译成一个.obj
包含机器代码指令的文件。当您包含一个.hpp
文件并使用在另一个文件中定义的某个函数时.cpp
文件,在链接阶段,链接器在相应.obj
文件中查找该函数定义。它就是这样找到它的。