C++动态链接库中的全局变量面试题

2023-05-16

其实主要问题是三个模块:

模块 a, 静态库 a
模块 b, 二进制 b, 静态引用a, 动态加载c
模块 c, 动态链接库c, 静态引用a

关键在于静态库a里有一个静态全局变量,没错就是我们的日志模块。原先的这个静态的模块中的静态全
局变量是有构造函数的,也就是构造函数干了点事情。我们都知道,程序载入在进入主函数前会依次初始
化全部的全局和静态变量。载入动态链接库时也不例外。这时候矛盾就来了,二进制b在进入主函数前会
初始化模块a中的全局变量,执行构造函数;然而载入动态链接库c时,也会启动对c内的全局变量进行初
始化,也会执行同一个对象的构造函数。这样,一个对象就会执行两次构造函数。在我们的程序里,就是
第二次执行构造函数的时候把全局变量的成员置空了。导致的结果是,我们的模块一开始有效,正常运行
了一会会之后,就失效了。当然在c里,并没有构造函数一说,对象构造时除了内存分配,什么都没干,
所以在纯c里并不会出现问题。这是碰到的问题,但是是不是在所有环境里都这样呢?或者使用静态成员
函数又如何?以下做了一个简单的测试:

一、模块a
.h文件:

struct foo_class {
    int m;
    foo_class();
    ~foo_class();
    static foo_class _;
};

.cpp文件:

#include <cstdio>
#include "a.h"
foo_class foo_class::_;
foo_class::foo_class() {
    m = 10;
    printf("foo_class::foo_class(), this-> 0x%llx\n", this);
}
foo_class::~foo_class() {
    printf("foo_class::~foo_class(), this-> 0x%llx\n", this);
}

编译选项:

gcc -O0 -g -ggdb a.cpp -o libtest_a.a -c -fPIC

二、模块b:
.cpp文件:

#include <dlfcn.h>
#include <errno.h>
#include <cstdio>
#include <cstdlib>
#include "a.h"
int main() {
    void (*dll_func)() = NULL;
    char* error = NULL;
    foo_class::_.m += 1000;
    printf("&foo_class::_ = 0x%llx, foo_class::_.m = %d\n", &foo_class::_, foo_class::_.m);
    void* handle = dlopen("libtest_c.so", RTLD_LAZY);
    if (!handle) {
        fprintf(stderr, "%s\n", dlerror());
        return -1;
    }
    dlerror();
    *(void **) (&dll_func) = dlsym(handle, "dll_func");
    if ((error = dlerror()) != NULL)  {
        fprintf(stderr, "%s\n", error);
        return -2;
    }
    (*dll_func)();
    dlclose(handle);
    return 0;
}

编译选项:

gcc -O0 -g -ggdb b.cpp -o test_b -fPIC -ldl -L$PWD -ltest_a -lstdc++

三、模块C:

.cpp
#include <cstdio>
#include "a.h"
extern "C" {
void dll_func() {
    foo_class::_.m += 100;
    printf("&foo_class::_ = 0x%llx, foo_class::_.m = %d\n", &foo_class::_, foo_class::_.m);
}
}

编译选项:

gcc -O0 -g -ggdb c.cpp -o libtest_c.so -shared -fPIC -L$PWD -ltest_a -lstdc++

这是三个模块的代码和编译选项。我分别至于Linux和Windows内的GCC编译测试。
在Linux中的GCC 4.4.6 运行结果如下:

foo_class::foo_class(), this-> 0x600f98
&foo_class::_ = 0x600f98, foo_class::_.m = 1010
foo_class::foo_class(), this-> 0x600f98
&foo_class::_ = 0x600f98, foo_class::_.m = 110
foo_class::~foo_class(), this-> 0x600f98
foo_class::~foo_class(), this-> 0x600f98

从结果中可以看出来,在Linux中多个动态链接库和主程序引用的同一个全局变量(地址相同),但是每一个二进制实例都会完成一次构造。这就造成了同一个实例多次构造,导致我们最初碰到的结果。
在Windows中Cygwin的GCC 4.8.2 中运行结果如下:

foo_class::foo_class(), this-> 0x100406010
&foo_class::_ = 0x100406010, foo_class::_.m = 1010
foo_class::foo_class(), this-> 0x5aa426010
&foo_class::_ = 0x5aa426010, foo_class::_.m = 110
foo_class::~foo_class(), this-> 0x5aa426010
foo_class::~foo_class(), this-> 0x100406010

但是在Windows中,虽然每个动态链接库和主程序引用的同一个全局变量也各自都执行了一次构造。但是,每一个二进制内的全局变量,实际上并不是同一个。他们并不冲突,但是他们也不在一个内存区域内,所以即便是纯C下和Linux内的行为也不一样。这也就意味着,在Linux中,载入的动态链接库实际上可以直接使用外部框架或者其他模块的全局数据,但是在Windows下确是隔离的,不能直接访问到。另外, 我从另一篇文章上看到,这个行为与dlopen时flag是RTLD_GLOBAL还是RTLD_LOCAL有关。但是我这里实测没有任何变化。但是结果和编译选项-fPIC有关(原因去看gcc文档吧,我就不复述啦)。
PS: 如果不是直接使用的全局变量,而是直接使用函数接口,并且返回一个static的局部变量这种方式,测试结果也是一样的;
而且如果不是通过dlopen动态加载,而是通过编译时链接进去的话,也是构造了两次。
这里就不再另外贴出输出结果了。其实,根本问题是多个动态链接库里共享的内存对象的构造问题。在不同环境下有不同的行为,也许会藏地比较隐晦。着实是个坑呐。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++动态链接库中的全局变量面试题 的相关文章

  • 如何为 Apple 官方 SwiftUI 示例中的图表元素加上首显动画?

    0 概览 在 Apple 官方教程示例 Animating Views and Transitions 中 苹果为我们展示了如何为 SwiftUI 中的各种视图添加动画和过渡效果 在示例的最后 我们在完成 3 种不同数据类型 Elevati
  • SwiftUI 4.0(iOS 16)极简实现一个美美哒的多选 Toggle 按钮组

    概览 在 SwiftUI 4 0 之前 xff0c 想要实现如下效果的多选 全选 Toggle 按钮组是要写不少行代码滴 xff1a 不过 xff0c 在 iOS 16 之后我们仅用1行代码即可搞定以上所有 xff01 在某些场合下这非常有
  • SwiftUI 使用 UIPageViewController 翻页后出现空白的原因及解决

    问题现象 我们 SwiftUI 开发的 App 需要 UIPageViewController 的翻页功能 这可以非常方便的通过桥接 UIKit 到 SwiftUI 来搞定 不过 观察上图可以发现 App 翻页显示的并不太对 当用户通过右下
  • SwiftUI 中 TabView 如何原生使用类 UIPageView 的翻页样式?

    功能需求 我们知道 TabView 是 SwiftUI 中非常好用的布局组织容器 它可以分类组织视图并依次展示给用户 从 SwiftUI 2 0 开始 iOS 14 0 TabView 除了常规的以标签 Tab Label 样式显示外 还可
  • SwiftUI 设计和调试复杂界面的基本技巧示例

    功能需求 对于比较复杂的 SwiftUI 界面 我们需要在充分了解 SwiftUI 各个视图基本特性的同时 合理利用 Xcode 强大的预览 Preview 机制 实时且全面的测试所有场景下的显示情况 如上图所示 我们在 App 支持的每种
  • SwiftUI 如何动态条件显示和隐藏 Toolbar 按钮且不做无谓刷新

    功能需求 在 SwiftUI 中我们可以非常容易的定制导航栏 Toolbar 中按钮的显示 包括折叠 分组和按条件动态显示和隐藏等 如上图所示 我们仅用寥寥几行代码就实现了 SwiftUI 导航栏 Toolbar 按钮的折叠 分组和按条件动
  • SwiftUI 极简实现文本摆动弹性动画

    概览 SwiftUI 为我们来了界面设计和调试上的便利 xff0c 只需几行代码我们就能实现一个不错的文本动画效果 xff1a 如上图所示 xff0c 我们在 SwiftUI 中基本还没发力 xff0c 就实现了文本摆动弹性动画 这究竟是怎
  • 美团后端笔试2022.08.13

    文章目录 昨天刚刚笔试结束 xff0c 然后今天抽空给大家整理一下 xff0c 然后简单说一下思路 整场笔试下来 xff0c 整体难度一般 xff0c 只不过在第三题扑克牌游戏的时候进行的不是很顺利 xff0c 附加题难度一般 xff0c
  • SwiftUI 如何让文本自动支持查找和替换功能?

    概览 有些情况下 xff0c 我们需要为文本编辑器实现文本的查找和替换功能 xff08 find amp replace xff09 xff0c 如果完全靠自已撸码还是比较棘手的 所幸的是 xff0c 从 SwiftUI 4 0 xff08
  • SwiftUI 新 Alert 弹出窗口圆你文本输入之梦

    概览 小伙伴们都知道 xff0c 弹出 Alert 不能包含文本输入框是 SwiftUI 的阿喀琉斯之踵 Achilles Heel 当然 xff0c 这说的有些夸张了 x1f609 不过 xff0c Alert 不能包含 TextFiel
  • SwiftUI 4.0 新 LabeledContent 视图帮您解决所有对齐烦恼

    概览 在用 SwiftUI Form 设计 App 界面时 xff0c 最头疼的就是内部视图对齐的问题了 好不容易适配了 iOS 中的布局 xff0c 到了 iPadOS 或 MacOS 上却变得一团糟 有没有一劳永逸 xff0c 简单方便
  • Xcode 使用 Instruments 无法找到代码中耗时挂起操作的解决

    问题现象 Instruments 是一套非常有用的代码分析和调试利器 我们经常用它来查找 App 中的性能瓶颈 不过 有时 Instruments 却无法捕获到系统明显挂起或长耗时的方法 这是怎么回事呢 如上图所示 App 运行中系统 Se
  • 用 DISM 命令备份与还原 Windows 系统

    一 初始备份 xff08 例如 xff1a 把 C 分区的系统备份到 D 分区的 Win8BF 文件夹中 xff0c 备份文件名为 Win8Pro wim xff09 xff1a Dism Capture Image ImageFile D
  • Unresolved reference: viewModels,viewModels()方法找不到

    遇到的问题 最近在学习LiveData和ViewModel xff0c 跟着官网敲 xff0c 碰到了以下情况 span class token keyword private span span class token keyword v
  • 天干地支计算

    年的干支 方法一 xff1a 首先要能记住十大天干和十二地支 xff0c 十天干 xff1a 甲 乙 丙 丁 戊 己 庚 辛 壬 癸 xff1b 十二地支 xff1a 子 丑 寅 卯 辰 巳 午 未 申 酉 戌 亥 xff1b 天干地支纪年
  • 程序员们,AI来了,机会来了,危机也来了

    程序员们 xff0c AI来了 xff0c 机会来了 xff0c 危机也来了 1 人工智能真的来了 纵观古今 xff0c 很少有计算机技术能有较长的发展寿命 xff0c 大部分昙花一现 xff0c 比如 xff1a 昔日的DOS windo
  • 统信UOS 20 1050 记录一次openssl升级失败的排查过程

    目录 不能找到openssl最新版本的原因 fedora有相关module的介绍 一些yum module 常用的命令 xff1a 查看yum module 都有哪些 启用禁用module 总结 xff1a 后记 xff1a 通过yum l
  • iOS之富文本

    之前做项目时遇到一个问题 xff1a 使用UITextView显示一段电影的简介 xff0c 由于字数比较多 xff0c 所以字体设置的很小 xff0c 行间距和段间距也很小 xff0c 一大段文字挤在一起看起来很别扭 xff0c 想要把行
  • JetBrains IntelliJ IDEA 2022.2 使用 Java 17 运行时

    JetBrains 发布 了 IntelliJ IDEA 2022 2 xff0c 支持 Java 17 和最新的语言和框架 xff0c 如 Scala Kotlin Spring 6 和 Spring Boot 3 这个新版本使用了 Je
  • Linux生产者消费者模型实现

    转载请注明出处 xff1a https blog csdn net mymottoissh article details 84181224 任何语言提及到多线程同步都离不开生产者 消费者模型 这也是针对许多现实问题建模用到的基础模型 这一

随机推荐

  • 使用CMD启动JAR后,出现定时器不执行问题

    SpringBoot项目中 使用了 64 Scheduled注解开启一个定时任务 在windows系统启动时 开始输出正常 当执行到输出控制台日志时 有时候会直接卡住线程不执行了 查了一圈发现是CMD的快速编辑引起的线程阻塞 解决办法 1
  • Flink开发中遇到的问题及解法

    1 当Source是Kafka的时候 xff0c 如何设置Source Operator的并发度 xff1f 如果没有指定 xff0c Source Operator的个数与集群中的TaskManager的个数相等 如果手动设置 xff0c
  • vue初学者代码格式报错

    报错内容 xff1a Do not use built in or reserved HTML elements as component id header
  • Ubuntu 设置su密码

    在使用su命令获取超级权限的时候提示输入密码 xff0c 在安装ubuntu的时候只设置了用户密码 xff0c 没记得有其他密码 这里需要给root用户重新设置密码 xff1a sudo passwd 然后输入密码即可
  • Spark MLlib学习(二)——分类和回归

    MLlib支持多种分类方法 xff0c 如二分类 多分类和回归分析等 问题类型 支持的方法 二分类 线性SVM 逻辑回归 xff0c 决策树 xff0c 随机森林 xff0c GBDT xff0c 朴素贝叶斯 多分类 决策树 xff0c 随
  • 关于onNewIntent你应该知道的

    一 API描述如下 大概意思是当Activity被设以singleTop模式启动 xff0c 当需要再次响应此Activity启动需求时 xff0c 会复用栈顶的已有Activity xff0c 还会调用onNewIntent方法 并且 x
  • 程序猿的情话

    世界上最遥远的距离 xff0c 是我在if里你在else里 xff0c 似乎一直相伴又永远分离 xff1b 世界上最痴心的等待 xff0c 是我当case你是switch xff0c 或许永远都选不上自己 xff1b 世界上最真情的相依 x
  • SpringBoot JPA实践之EntityManage查询返回自定义DTO

    在很多时候我更喜欢随意组合查询出来返回一个DTO对象的实现 xff0c JPA提供的多数查询均以返回Entity居多 xff0c 它提供的EntityManager对象可以实现将SQL语句查询的结果转换为自定义DTO对象 xff08 这与
  • 经典编程书籍(C++, 网络, Windows, Linux)【转载】

    书单中列举的都是相关领域的经典书籍 xff0c 必读之作 此书单的编辑参考了很多网站 xff0c 包括一些名家的推荐 xff0c 例如侯捷 xff0c 孟岩 xff0c 荣耀 xff0c 潘爱民等等 xff0c 在此也向这些前辈表示感谢 1
  • SpringBoot微服务框架概述

    SpringBoot微服务框架 2 Spring Boot 微服务框架的特点3 Spring Boot 应用场景4 SpringBoot的第一个应用5 Springboot引导类的main方法有什么作用 xff1f 6 SpringBoot
  • 使用Spring的注解方式实现AOP

    Spring对AOP的实现提供了很好的支持 下面我们就使用Spring的注解来完成AOP做一个例子 首先 xff0c 为了使用Spring的AOP注解功能 xff0c 必须导入如下几个包 aspectjrt jar aspectjweave
  • 如何正确有效的学习一门计算机语言?

    在这个互联网高速发展的社会 xff0c 越来越多的人喜欢上了IT行业 xff0c 认为计算机行业是一个高科技的 高薪的行业 的确如此 xff0c 但是系统的学习一门计算机语言 xff0c 并且把它用运用到真正的开发中去还真不是一件简单的事情
  • (二)为AI和机器学习创建Docker容器

    目录 介绍 基本镜像 创建Dockerfile 构建镜像 运行容器 总结 下载源文件 154 4 KB 介绍 Docker 等容器技术显着简化了软件的依赖管理和可移植性 在本系列文章中 xff0c 我们探讨了 Docker 在机器学习 ML
  • 自定义EF Core迁移历史记录表

    目录 背景 更改表名称和架构 更改列名称 添加具有默认值的列 添加必填列 关于代码示例 引用 下载源代码 13 9 KB 背景 实体框架核心通过在名为 EFMigrationsHistory 和架构 dbo 的表中添加日志来跟踪应用的迁移
  • 使用EF Core 6执行原始SQL查询

    目录 背景 现有选项 ExecuteSqlRaw 插入 更新 删除 FromSqlRaw FromSqlInterpolated 自定义数据库上下文扩展方法 ExecuteScalar ExecuteNonQuery FromSqlQuer
  • 仓库更新了,git pull拉取远端失败

    仓库更新了 span class token punctuation span git pull拉取远端失败 span class token operator span error span class token operator sp
  • xPath 用法总结整理

    最近在研究kafka xff0c 看了一堆理论的东西 xff0c 想动手实践一些东西 xff0c 奈何手上的数据比较少 xff0c 突发奇想就打算写个爬虫去抓一些数据来玩 xff0c 顺便把深入一下爬虫技术 之前写过一些小爬虫 xff0c
  • vi编辑器

    目录 简介 基本操作 1 移动光标 2 定位 3 删除 4 复制 5 查找
  • 使用libevent搭建简单http服务器

    64 使用libevent搭建简单http服务器 一 libevent 的下载 可通过官网直接进行下载libevent库 xff1b 例子使用的是V2 1 1版本 xff0c 下载完成后 xff0c 解压 xff1b 可以再解压目录下获取
  • C++动态链接库中的全局变量面试题

    其实主要问题是三个模块 xff1a 模块 a 静态库 a 模块 b 二进制 b 静态引用a 动态加载c 模块 c 动态链接库c 静态引用a 关键在于静态库a里有一个静态全局变量 xff0c 没错就是我们的日志模块 原先的这个静态的模块中的静