C/C++标题和实现文件:它们如何工作?

nic*_*pro 40 c++ compilation header-files

可能是一个愚蠢的问题,但我现在在网上和网上搜索了很长一段时间并且无法得出一个明确的答案(我的尽职调查谷歌搜索).

所以我是编程的新手......我的问题是,主函数如何知道不同文件中的函数定义(实现)?

恩.说我有3个文件

  • main.cpp中
  • myfunction.cpp
  • myfunction.hpp

//main.cpp

#include "myfunction.hpp"
int main() {
  int A = myfunction( 12 );
  ...
}
Run Code Online (Sandbox Code Playgroud)

-

//myfunction.cpp

#include "myfunction.hpp"
int myfunction( int x ) {
  return x * x;
}
Run Code Online (Sandbox Code Playgroud)

-

//myfunction.hpp

int myfunction( int x );
Run Code Online (Sandbox Code Playgroud)

-

我得到预处理器如何包含头代码,但是头和主函数如何知道函数定义是否存在,更不用说它了?

如果不清楚或者我对这里的新事物有很大误解,我会道歉

Ed *_*eal 63

头文件声明函数/类 - 即告诉编译器何时编译.cpp文件可用的函数/类.

.cpp文件定义了这些函数 - 即编译器编译代码,因此生成实际的机器代码以执行在相应.hpp文件中声明的那些操作.

在您的示例中,main.cpp包含一个.hpp文件.预处理器将替换文件#include的内容.hpp.该文件告诉编译器该函数myfunction在别处定义,它接受一个参数(an int)并返回一个int.

因此,当您编译main.cpp成目标文件(.o扩展名)时,它会在该文件中记下它需要该函数myfunction.当您编译myfunction.cpp成目标文件时,目标文件中有一个注释,其中包含其定义myfunction.

然后,当你连接两个目标文件来组合成一个可执行,链接关系的结束了-即main.o使用myfunction中定义myfunction.o.

我希望有所帮助


Cor*_*ren 15

从用户的角度来看,您必须了解编译是一个两步操作.


第一步:对象编译

在此步骤中,*.c文件将单独编译为单独的目标文件.这意味着当编译main.cpp时,它对myfunction.cpp一无所知.他唯一知道的是你声明一个带有这个签名的函数:int myfunction( int x )存在于另一个目标文件中.

编译器将保留此调用的引用并将其直接包含在目标文件中.对象文件将包含"我必须使用int调用myfunction,它将返回给我一个int.它保留所有extern调用的索引,以便能够在之后与其他链接.


第二步:链接

在此步骤中,链接器将查看目标文件的所有索引,并尝试解决这些文件中的依赖项.如果不在那里,你就会从中获得名声undefined symbol XXX.然后,他将这些引用转换为结果文件中的实际内存地址:二进制文件或库.


然后,您可以开始询问如何使用像Office套件这样拥有大量方法和对象的巨大程序来实现这一目标?好吧,他们使用共享库机制.您可以使用Unix/Windows工作站上的".dll"和/或".so"文件来了解它们.它允许推迟解决未定义的符号,直到程序运行.

它甚至允许使用dl*函数按需解决未定义的符号.


Mat*_* M. 5

1.原则

当你写:

int A = myfunction(12);
Run Code Online (Sandbox Code Playgroud)

这被翻译为:

int A = @call(myfunction, 12);
Run Code Online (Sandbox Code Playgroud)

在哪里@call可以看作字典查找.如果你考虑字典类比,你可以在知道它的定义之前知道一个单词(smogashboard?).您只需要在运行时将定义放在字典中.

2.关于ABI的观点

这个@call是如何工作的?因为ABI.ABI是一种描述许多内容的方法,其中包括如何执行对给定函数的调用(取决于其参数).调用契约很简单:它只是说明了每个函数参数的位置(一些将在处理器的寄存器中,另一些在堆栈中).

因此,@ call实际上做了:

@push 12, reg0
@invoke myfunction
Run Code Online (Sandbox Code Playgroud)

函数定义知道它的第一个参数(x)位于reg0.

但我虽然字典是动态语言的?

你是对的,在某种程度上.动态语言通常使用用于动态填充的符号查找的哈希表来实现.

对于C++,编译器将转换单元(粗略地说,预处理的源文件)转换为对象(.o.obj一般).每个对象都包含一个它引用的符号表,但其定义未知:

.undefined
[0]: myfunction
Run Code Online (Sandbox Code Playgroud)

然后链接器将对象组合在一起并重新对齐符号.此时有两种符号:

  • 那些在库内的,可以通过偏移引用(最终地址仍然是未知的)
  • 那些在库外的,并且其地址在运行时之前是完全未知的.

两者都可以以相同的方式对待.

.dynamic
[0]: myfunction at <undefined-address>
Run Code Online (Sandbox Code Playgroud)

然后代码将引用查找条目:

@invoke .dynamic[0]
Run Code Online (Sandbox Code Playgroud)

当库被加载(DLL_Open例如),运行时会终于知道其中的符号映射到存储器中,并覆盖<undefined-address>与真实地址(这个运行).