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


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

    工程A的主要实现代码:
    int g_int = 0; // 全局变量
    int CStaticClass::GetGlobalValue()
    {
    return g_int;
    }
    工程B的主要实现代码:
    // DLL.H
    class DLL_API CDLL {
    }
    // DLL.CPP
    extern 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保证只有一份静态库的实现。

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

    测试代码

  • 相关阅读:
    svn服务器的搭建和使用以及git服务器的搭建和使用
    MySQL Performance Schema详解
    Lua集成Redis及Nginx
    分布式系统下的CAP定理
    分布式事务一站式解决方案与实现
    Zookeeper集群搭建及原理
    Redis主从复制搭建及原理
    vue中给img的src添加token
    调度器34—RT负载均衡 Hello
    tracer ftrace笔记(5)—— 使用笔记汇总 Hello
  • 原文地址:https://www.cnblogs.com/feihe0755/p/5611218.html
Copyright © 2020-2023  润新知