碰到一个动态链接的问题,下面是源码hello.h,hello.cpp,apis.h和main.cpp以及Makefile。
hello.h,hello.cpp,apis.h生成动态链接库libhello.so,main.cpp动态链接libhello.so。
在hello.cpp中定义了一个全局变量data,并且在hello类的构造函数中给data添加了数据,然后在apis.h中定义了hello的一个实例和一个api
#pragma once
//hello.h
class hello
{
public:
hello();
};
//hello.cpp
#include "hello.h"
#include <vector>
#include <iostream>
std::vector<int> data;
std::vector<int> *pdata;
hello::hello()
{
data.push_back(1);
data.push_back(2);
std::cout << "in hello " << data.size() << std::endl;
pdata = new std::vector<int>;
pdata->push_back(1);
pdata->push_back(1);
std::cout << "in hello " << pdata->size() << std::endl;
}
//apis.h
#include "hello.h"
#include <vector>
#include <iostream>
extern std::vector<int> data;
extern std::vector<int>* pdata;
hello h;
void test()
{
std::cout << "in test " << data.size() << std::endl;
std::cout << "in test " << pdata->size() << std::endl;
}
//main.cpp
#include <iostream>
#include <vector>
using namespace std;
void test();
extern vector<int> data;
extern vector<int>* pdata;
int main()
{
cout << data.size() << endl;
cout << pdata->size() << endl;
test();
return 0;
}
#makefile
a.out: libhello.so main.cpp
g++ main.cpp ./libhello.so -o a.out
libhello.so: hello.cpp apis.cpp hello.h
g++ apis.cpp hello.cpp -fPIC -shared -o libhello.so #1
#g++ hello.cpp apis.cpp -fPIC -shared -o libhello.so #2
clean : rm libhello.so a.out
现在问题出现了:
使用Makefile中的指令#1处时,得到的结果如下,也就是说变量data.size()的结果为0,而pdata->size()的结果为2
使用Makefile中的指令#2处时,得到的结果如下,也就是说变量data.size()的结果为2,而pdata->size()的结果为2
在《程序员的自我修养》一书的7.3.4中专门介绍了共享模块全局变量的问题(注:以下内容引述自该小节内容以)
当一个模块引用了一个定义在共享对象的全局变量时,无法根据上下文判断该变量是定义在共享模块内部还是其他模块。因为程序主模块也可能引用该全局变量且程序的主模块代码不是PIC(地址无关代码),故该变量的地址在链接过程中确定下来,在可执行文件的.bss段中创建了一个副本。编译器的解决方案是所有使用该变量的指令都指向可执行文件中的那个副本,且ELF共享库在编译时默认把定义在模块内部的全局变量当做定义在其他模块的全局变量,通过GOT来实现变量的访问,这样变量在运行时始终只有一个实例,如果变量在共享模块中被初始化,那么动态链接器还需要将初始化值复制到程序主模块中的变量副本。
回到我的问题上,猜测两种编译方式导致不同结果的原因可能是因为模块编译顺序的不同导致没有正确的初始化data变量。至于为什么会这样,需要查看编译出来模块的初始化代码了。