?
最近在阅读《大规模C++ 程序设计》 在第1部分,作者讨论了内链接和外链接问题(因为大规模的C++程序有繁多的类和单元。因此编译速度是个大问题)
这里记录一下关于内链接和外链接的理解。
?
?
我们以bcb 和 vs 为例,一个程序文档一般都由 .cpp 文件 和 .h文件构成。但编译时,只有.cpp 参与编译。.h文件则会被预编译器复制到引用他的.cpp中。
然后,.cpp文件被编译成.obj文件。
接着,通过链接器,将obj文件链接为exe文件。
?
?
当经过编译分别得到a.obj 和 b.obj, 链接器发现b.obj引用了一个全局变量。则要去其他的.obj文件中搜索这个变量的定义。
如果没有搜索到,则会报未定义的外部符号。然后无法编译完成。
?
?
所谓外部链接,就是被外部引用的符号。这么说太抽象。下面是一个具体的例子:
?
用vs建立一个VC++的控制台工程。
#include <iostream> using namespace std; |
?
?
#include "stdafx.h" ? int nA = 100; |
#include "stdafx.h" ? extern int nA; ? void printnA() { ????cout<<nA<<endl; } |
完全正常,那么链接器在链接b.obj的时候是如何知道这个nA 在哪里定义的呢?
其本质,是因为我们指定了extern int nA 告诉链接器,这个变量是从外部引用的。
链接器说:好,我去找找。于是就找到了a.obj。这就是外部链接过程。
#include "stdafx.h" ? //extern int nA; int nA = 200; ? void printnA() { ????cout<<nA<<endl; } |
?
编译时,链接器则会输出错误
错误????1????error LNK2005: "int nA" (?nA@@3HA) 已经在 a.obj 中定义????D: \externtest\externtest\b.obj????externtest 错误????2????error LNK1169: 找到一个或多个多重定义的符号????D:\externtest\Debug\externtest.exe????1????1????externtest |
?
这又是为什么呢? b.cpp 和 a.cpp 中nA 定义冲突了。
?
其本质原因是,在cpp中定义的外部变量,都是要参与外链接的。前面说了链接器会检查有没有重定义。
他现在a.obj中发现了 int nA, 接着又在 b.obj中发现了int Na,重复的外部符号。因此就报错了。
?
#include "stdafx.h" ? //extern int nA; //int nA = 100; static int nA = 100; ? void printnA() { ????cout<<nA<<endl; } |
?
我们将int nA 声明为静态的。编译链接就完全没有问题。为什么呢?
因为,静态的定义,都是内部链接。因此,链接并不会去检查重复。
(当然,如果在一个.cpp中定义了2个同名的静态变量,编译阶段就通不过,此时是编译器校验而不是链接器校验)
?
#include "stdafx.h" ? //extern int nA; //int nA = 100; extern int nA; static int nA = 100; ? void printnA() { ????cout<<"nA in b.cpp"<<nA<<endl; } |
?
再修改a.cpp
#include "stdafx.h" ? int nA = 300; ? void PrintnA2() { ????cout<<"nA in a.cpp"<<nA<<endl; } |
?
修改main函数
void printnA(); void PrintnA2(); ? int _tmain(int argc, _TCHAR* argv[]) { ????printnA(); ????PrintnA2(); ????return 0; } |
?
得到这样的结果
?
?
这时的情况是:b.cpp中外部引用了一个nA,还定义了一个静态内部Na.
但并不冲突,事实情况是,在b.cpp中凡是引用nA的地方,都引用的内部nA,既static 声明的nA
在a.cpp中,则依然是引用的全局的Na.
?
我们可以看到,c++ 对于同时存在内部和外部同名符号时,只认内部符号。
我们修改main.cpp 用extern声明完全没有问题。
?
? #include "stdafx.h" ? void printnA(); void PrintnA2(); //void printnA3(); extern void printnA3(); ? int _tmain(int argc, _TCHAR* argv[]) { ????printnA(); ????PrintnA2(); ????printnA3(); ????return 0; } |
?
?
这说明,函数也是外部链接的。只要声明了函数,链接器就去其他的obj中查找外部引用。
#include "stdafx.h" ? //extern int nA; //int nA = 100; extern int nA; static int nA = 100; ? void printnA() { ????cout<<"nA in b.cpp"<<nA<<endl; } ? void printnA3() { ????cout<<"::nA in b.cpp"<<::nA<<endl; } ? static void printnA4() { ????cout<<"static printnA4 in b.cpp"<<::nA<<endl;???? } |
?
接着修改 main.cpp
?
#include "stdafx.h" ? void printnA(); void PrintnA2(); //void printnA3(); extern void printnA3(); void printnA4(); ? int _tmain(int argc, _TCHAR* argv[]) { ????printnA(); ????PrintnA2(); ????printnA3(); ????printnA4(); ????return 0; } |
?
????编译,我们会得到一个报错
????
?
当我们把 b.cpp中的static注释,又会恢复正常。
?
和静态定义的变量一样。静态的函数也是内链接的。并不会被外链接访问(可以这么理解,当声明一个函数为static,就不想让外部(其他的obj)访问他)
?
请注意 ::nA 这个写法,在b.cpp中并不会因为写了::nA 就访问去全局的nA了
#include "stdafx.h" ? //extern int nA; //int nA = 100; extern int nA; static int nA = 100; ? void printnA() { ????cout<<"nA in b.cpp"<<nA<<endl; } ? void printnA3() { ????cout<<"::nA in b.cpp"<<::nA<<endl; } ? //static void printnA4() { ????cout<<"static printnA4 in b.cpp"<<::nA<<endl;???? } ? inline void printnA5() { ????cout<<"inline printnA5 in b.cpp"<<::nA<<endl; } |
?
?
同样,在main.cpp中去引用printnA5
? #include "stdafx.h" ? void printnA(); void PrintnA2(); //void printnA3(); extern void printnA3(); void printnA4(); void printnA5(); ? int _tmain(int argc, _TCHAR* argv[]) { ????printnA(); ????PrintnA2(); ????printnA3(); ????printnA4(); ????printnA5(); ????return 0; } |
?
同样得到链接错误
?
这说明内联函数,也是无法参与外链接的。
?
我们现在没有涉及类。仅仅涉及了C中的一些特性。但已经知道了什么内链接和外链接。
?
我的理解是:外链接就是要让连接器去其他的obj中查找符号的引用行为。内链接则是不需要链接器去其他obj查找的引用行为。
?
?
会发生外链接的行为(涉及到类之后,会有更多情况,这里只是一部分)
?
?
仅内链接的行为
?
?
原文:http://www.cnblogs.com/songr/p/5222768.html