c/c++中静态链接过程中对未被代码引用的符号的检查

看到一个应该关注的知识点。
之前看到的一个帖子:
[url]http://my.unix-center.net/~Simon_fu/?p=263[/url]
大致情况讲的都比较清楚了
a.h:
/*******************************************/

#include <string>

using namespace std;

class A
{
public:
A(void);
A(const char *s);
~A(void);
private:
string name;
};
/**********************************************/
a.cpp:
/**********************************************/
#include "a.h"
#include <iostream>
using namespace std;
A::A(void)
{
cout<< "A construct!\n";
}
A::A(const char *s)
{
name = string(s);
cout<< "A " + name + " construct!\n";
}
A::~A(void)
{}
static A g_a("globalA");
/************************************************/

假设上述代码编译成一个静态库lib.a,该静态库中声明了一个全局变量,预期加载该静态库的时候该全局变量会被创建。
另外创建一个测试的工程来加载这个静态库:

test_staticlib.cpp:
/*************************************************/
#include <stdio.h>
#include "../static_lib/a.h"
int main()
{
//A a;
//A aa("Local A");
getchar();
}


这个代码在输出时是不会创建全局变量g_a,这个很好理解,因为虽然test_staticlib.cpp include了a.h,但是代码链接时并没发现需要链接a.h中声明的符号,因此实际不会链接那个静态库。
当代码为:
int main() 
{
A a;
//A aa("Local A");
getchar();
}

则可以发现静态变量g_a会被创建,这里应该思考一下,为何在声明"A a;"之后,静态变量g_a会被创建,而之前的代码却不会?
原因在于代码test_staticlib.cpp引入了A a,这导静态链接时致链接程序需要在参数路径下的目标文件或者lib中找到A的代码块进行链接,当在发现A定义位于lib.a之中时,它会同时将lib.a中所有定义的符号找到对应的代码块。
所以第一段代码虽然include了a.h,但实际没有出现过g_a的符号,所以即使include了,也不会创建变量g_a。
而第二段代码由于引入了符号a需要链接lib.a中定义的对象A的代码块,导致了链接器对lib.a中[color=red]所有符号[/color]的链接检查,发现还存在静态全局变量g_a,于是将g_a也静态链接(拷贝)到执行文件当中。注意这里的“所有”,即使是那些main根本没有引用到的符号,链接程序也会一一查清楚这些符号是否有对应的模块代码存在。
举个例子就比较清楚了:

a.h:
/*******************************************/

#include <string>

using namespace std;

class A
{
public:
A(void);
A(const char *s);
~A(void);
private:
string name;
};
extern int k; <-------------------------------------
void fn(); <-------------------------------------
/**********************************************/
a.cpp:
/**********************************************/
#include "a.h"
#include <iostream>
using namespace std;
A::A(void)
{
cout<< "A construct!\n";
}
A::A(const char *s)
{
name = string(s);
cout<< "A " + name + " construct!\n";
}
A::~A(void)
{}
static A g_a("globalA");
void fn(){k++;} <-------------------------------------
/************************************************/

生成静态库的代码添加箭头部分代码。
该静态库可以成功生成,因为k是作为外部定义的变量,其符号不会在链接器生成lib时去找对应的定义k的代码块链接。
但此时再重新编译链接test_staticlib.cpp会发现会报k缺少引用的错误:
undefined reference to `k'
可以看到尽管test_staticlib.cpp的main函数代码中完全没使用到fn(),但由于对lib.a的链接不仅仅链接了A代码模块,也顺带查找了fn中k符号的链接位置,导致编译器报错。
所以这牵扯到一个问题:如果你的代码中的符号需要链接lib.a中的某段代码,那么你也要同时确定这个lib.a中所有出现的其他符号能顺利找到对应的链接代码块,这种确定性检查会级联到所有和它直接或间接关联的库。

原作者的文章说gcc和ms的链接策略不一样,他在gcc中即使main中没有a.cpp中定义的符号,只包含a.h也能创建g_a,但在我机器上mingw4.5.2上gcc和vs表现是一样的。

static A g_a("globalA")这个定义只在lib.a中存在,因此如果main函数没任何符号会需要跨模块访问其他obj或者lib文件,是没有道理去查看lib.a,并且能意识到应该链接static A g_a("globalA")的符号定义的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值