“separate compilation” & “Linkage”

1. 先来看段代码

//file1.cpp
#include 
using std::cout;
int x=1;
void f(){

     cout<




//file2.cpp
void f(); // extern void f(); the same.
int main(int argc, char const *argv[])
{
     f();
     return 0;
}



如果单独编译两个文件,能够得到结果。
如果,再把编译得到的两个object链接,也是没有问题的。

对于file2.cpp,如果按照我以前的想法,我觉得这里为什么不需要include 一个header呢?

2. compilation & Linkage

先说编译。
我们的编译器,只读取一个文件和它的header作为输入!!
然后进行预处理,预处理之后的结果叫做一个translation unit.


比如,我们来看编译file1.cpp的预处理。
会读到include了一个header。
然后,编译器会把header里面的内容,放在file1.cpp里面的文件内容的前面。
变成了一个新的文件。

但是,注意,这可能是一个recursive的过程。
因为在这个被include的header里面,可能还include了其他header。
那么,又要进行一次展开。

最后,会得到一个很长,很长的文件。

然后,编译器还要对一些macros进行替换。
最后替换结束后,得到的文件,就叫做translation unit。



现在假如来编译file2.cpp,预处理的阶段什么都不需要做。
没有include header,也没有任何macros。
那么也得到了一个translation unit。


接下来,我们要做的事情是开始对translation unit开始编译了。


编译是对每一个translation unit单独进行的。
编译的时候,编译器只看当前正在编译的translation unit里面的内容。


现在来看下编译file2.cpp的过程:

编译器,读到了一个函数的声明。
注意,在这里这个函数是在namespace scope(file scope)。
那么,它会被默认为external linkage.
意思是这个函数的可以被不同的translation unit里面的代码使用。
那么,这个函数的定义,可能在file2.cpp里面,也可能在其他,一会会一起链接的文件里面。
再往下面走,就没有什么特别了。



同理。
在file1.cpp里面。
x和f都有external linkage。

也因此,我们能够在file2.cpp的translation unit使用f。
但是,如果我们要在file2.cpp里面,使用x,该怎么办呢?

因为int的声明和定义是一起的。
如果,我们一旦有了声明,那么我们实际上在file2.cpp里面就重新定义了x。
所以,如果要在file2.cpp里面使用x,那么需要做的是:
extern int x;

实际上,我们在file2.cpp里面,声明函数f,也是可以加上extern的:

extern void f();

只是,因为它们在namespace scope里面,默认了是extern linkage的。
不需要特别指定。



另外,也注意到,如果我们要有一个file1.hpp
那么这个file1.hpp里面要有什么内容呢?
其实就很好想了:

//file1.hpp
void f();
extern int x;




于是,file2.cpp就可以写成:

//file2.cpp
#include "file1.hpp"
int main(int argc, char const *argv[])
{
     f();
     return 0;
}


那么,编译器在编译的时候,会把file1.hpp的内容,在file2.cpp中展开,得到file2对应的translation unit。



顺便提一下,f()对于c++和c来说是不同的。
一个是没有arguments,一个是可以有数量不限的arguments


ref: the c++ programming language   9.1&9.2



你可能感兴趣的:(c++,c++)