跳转至

c++静态库和动态库全局变量初始化有何不同?

采用动态链接库的问题:版本升级,调用动态库的程序需不需要重新编译,需要考虑动态链接库二进制ABI兼容性问题,比如是否更改了头文件中的结构体,更改了接口函数,添加了虚函数。动态链接库变更以后,如果没有热更新so,需要替换so,重启可执行程序。

静态库的问题,考虑是否存在资源加载多次的问题。

1 静态库初始化两次

#include <cstdio>

#include "A.h"

#include "iBackend.h"

ClassA A1;    // 我们在这里,定义了全局变量

ClassA::ClassA() {

    printf("ClassA\n");

}

ClassA::~ClassA() {

    printf("~ClassA\n");

}

void ClassA::test() {}

REGISTER_BACKEND(A) // 我们在这里,定义了全局变量
#include <cstdio>

#include "A.h"

#include "B.h"

int main() {

    printf("main()\n");

    ClassA::test();    // 本行保证和A够成链接关系

    ClassB b;          // 本行保证和B构成链接关系

    printf("main: END\n");

    return 0;

}
ClassA

call Register : A

ClassA

call Register : A

main()

ClassB

main: END

~ClassB

~ClassA

~ClassA

2 静态库不初始化全局变量

链接器并没有生成这个自动初始化的代码,因为链接器觉得这几个“没有”被使用的全局对象不需要,所以就没生成。

https://www.cppblog.com/kevinlynx/archive/2010/01/17/105885.aspx

#include <cstdio>

#include "A.h"

#include "B.h"

int main() {

    printf("main()\n");

    // ClassA::test();    // 本行保证和A够成链接关系

    // ClassB b;          // 本行保证和B构成链接关系

    printf("main: END\n");

    return 0;

}
main()

main: END

解决方法:

主要包括两种,定义dummy函数加static局部变量,由外部显示调用。或者通过链接.a时设置-Wl,-whole-archive标志。

https://litaotju.github.io/c++/2020/07/24/Whole-Archive-in-static-lib/

如果可执行程序或者动态链接库加入了-Wl,-whole-archive链接.a文件的时候,报错.a用到的xxx等库未定义,可以考虑交换链接顺序。

# add_definitions("-Wl,-u,needed_symbol")

# add_definitions("-Wl,--whole-archive")

add_library(app SHARED ${AI_SRC})

target_link_libraries(app PUBLIC

    -Wl,-whole-archive

    xxx.a

    -Wl,-no-whole-archive

    GTest::gtest_main

    yyy

    dl)

install (TARGETS app

    RUNTIME DESTINATION lib    

)

3 动态库不初始化全局变量

#include <cstdio>

#include "A.h"

#include "B.h"

int main() {

    printf("main()\n");

    // ClassA::test();    // 本行保证和A够成链接关系

    // ClassB b;          // 本行保证和B构成链接关系

    printf("main: END\n");

    return 0;

}
main()

main: END

4 动态库初始化一次全局变量

#include <cstdio>

#include "A.h"

#include "B.h"

int main() {

    printf("main()\n");

    ClassA::test();    // 本行保证和A够成链接关系

    ClassB b;          // 本行保证和B构成链接关系

    printf("main: END\n");

    return 0;

}
ClassA

call Register : A

main()

ClassB

main: END

~ClassB

~ClassA

记一次BUG调试——静态链接库中全局变量/静态变量被重复初始化

https://cloud.tencent.com/developer/ask/sof/97688

5 两个动态库加载同一个静态库

那个两个动态库引用同一个静态库问题? - 知乎

如果不走dlopen、dlsym,而是在g++命令里显式链接的话 (g++ -o main main.cpp -L./ -lsingleton_lib -ldyn_test_1 -ldyn_test_2),又是只创建一个singleton_test实例

为什么静态局部变量是共用的一份?

dlopen传入的flagRTLD_LOCAL切换为RTLD_GLOBAL,输出就不一样了。

https://cloud.tencent.com/developer/article/1179871

多个c/c++动态库函数同名冲突解决方法_c++同名函数冲突-CSDN博客

https://www.cnblogs.com/oloroso/p/6273295.html

不同的编译选项,不同的链接器,采用不同加载so的方式(编译和dlopen),不同的编译器(gcc、clang)可能结果都不一样,

在静态库中最好不要去存放全局变量,也不要在这里创建单例对象等。

https://joydig.com/research-global-variables-desctruction-behavior-in-cpp/

6 交叉编译中忽略so中的undefined xxx

target_link_options(xxx PRIVATE "-Wl,--unresolved-symbols=ignore-in-shared-libs")

其它

# 为了使x86平台上so中的static变量和demo使用同一个
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -rdynamic")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -rdynamic")

评论