关于静态库中使用全局变量可能导致的问题

来源:互联网 发布:鲜卑化 知乎 编辑:程序博客网 时间:2024/04/27 16:29

同事找我看一个问题,一个访问全局变量不符合预期的问题。
因为新工程中静态库动态库非常多,非常不利于分析问题。
再因为并不是一个业务逻辑问题,而是一个语言层面的问题,所以我单独抽象出产生问题的环境,简化问题,更容易分析。
刚开始,是一个方案,五个工程,能够复现问题。
然后继续缩减三个工程,依然能够复现问题。
三个工程分别为静态库A,DLL B,EXE C。三者的依赖关系为:B依赖A, C依赖A和B。

复制代码
工程A的主要实现代码:int g_int = 0; // 全局变量int CStaticClass::GetGlobalValue(){return g_int;}工程B的主要实现代码:// DLL.Hclass DLL_API CDLL {}// DLL.CPPextern int g_int;CDLL::CDLL(void){m_pMyClass = new CMyClass();m_pStatic = new CStaticClass();g_int = 1;}CDLL::~CDLL(void){delete m_pMyClass;m_pMyClass = NULL;}CMyClass* CDLL::GetClassPtr(){return m_pMyClass;}工程C的主要实现代码:extern int g_int;int _tmain(int argc, _TCHAR* argv[]){g_int = 2;CDLL dll;CMyClass* pClass = dll.GetClassPtr();int n = pClass->Get(); // 这里的n为2,即不是1,也不是0}
复制代码

使用最少的代码复现问题,可以将问题集中在更小的方面,便于分析。
工程还可以进一步简化,手动将静态库中的类CStaticClass在两个工程B, C中实现。
然后调试代码时,进入int n = pClass->Get(); 进入Get()函数实现里,我们可能看到进入的是EXE工程的实现代码。
虽然指针是从DLL中导出来的,但是调用的却是EXE中的实现代码。
为什么?因为DLL和EXE都是独立的可执行代码。
如果DLL导出了CMyClass类,且EXE中没有CMyClass的实现代码,自然会去调用DLL中的实现代码。
如果DLL没有导出CMyClass类,且EXE中有CMyClass类的实现代码,那么自然会调用EXE中的实现代码。
如果DLL导出了CMyClass类,且EXE中也有CMyClass类的实现代码,则链接的时候会报重复定义的错误。
所以,如果调用的是静态库中的类函数的实现,则自然使用DLL中的全局变量。
如果调用的是EXE中的实现,则自然是访问EXE中的全局变量。
问题的解决方案:
方案1、静态库中去掉全局变量,改用其他方式。
方案2、静态库改成动态库。
方案3、整个Solution保证只有一份静态库的实现。

个人觉得静态库有太多实现,总感觉不太安全,觉得静态库只有一份实现比较好。
如果有多份实现,最好用动态库。
如果感觉上面比较麻烦,静态库中最好不要有全局变量。

测试代码

0 0