【问题标题】:Both static variables and global variables show different addresses in dynamic library and static library on Linux?静态变量和全局变量在Linux上的动态库和静态库中显示不同的地址?
【发布时间】:2015-11-10 02:58:09
【问题描述】:

我在 CentOS 6.5 上遇到过。正如我在网上搜索的那样,静态变量在使用动态库时在 Windows 和 Linux 上的行为不同。也就是说,Windows 会导致变量重复,而 Linux 不会,如下所示: http://www.yolinux.com/TUTORIALS/LibraryArchives-StaticAndDynamic.html

但是,当我写了一个小程序来验证这一点时,我发现 Linux 也会导致重复。这是我的小程序,包括四个文件:

(1) 啊.h

#ifndef A_H
#define A_H
#include <cstdio> 
static int b; 
extern "C" class A { 
    public:
    int mem;
    A() {
        printf("A's address: %p\n", this);
        printf("B's address: %p\n", &b);
    }
    void print() {
        printf("%p: %d\n", this, mem);
    }
    ~A() {
        printf("DELETE A!!!!! %p\n", this);
    }
}; 
extern A a;
#endif

(2) A.cpp

#include "A.h"
A a;

(3) d.cpp

#include "A.h"
extern "C" void exec() {
    a.print();
}

(4) main.cpp

#include "A.h"
#include <dlfcn.h>
typedef void (*fptr) ();
int main() {
    a.mem = 22;
    a.print();
    void *handle;
    handle = dlopen("d.so", RTLD_LAZY);
    fptr exec = reinterpret_cast<fptr>(dlsym(handle, "exec"));
    (*exec)();
    dlclose(handle);
    return 0;
}

这是我编译和运行程序的方式:

g++ d.cpp A.cpp -shared -rdynamic -o d.so -ldl -I. -fPIC -g -std=c++1y
g++ main.cpp A.cpp -ldl -I. -g -std=c++1y
./a.out

动态部分d.cpp 和静态部分main.cpp 都使用A.cppA.h 中声明的变量ab。这是我机器上程序的结果:

A's address: 0x600f8c
B's address: 0x600f90
0x600f8c: 22
A's address: 0x7fb8fe859e4c
B's address: 0x7fb8fe859e50
0x7fb8fe859e4c: 0
DELETE A!!!!! 0x7fb8fe859e4c
DELETE A!!!!! 0x600f8c

这让我很惊讶,因为全局变量a和静态变量b的地址在动态部分和静态部分应该是一样的。似乎对静态部分的a 的修改不会影响动态部分的a。有人可以回答我的问题,或者帮助找出程序中的一些错误(如果有的话)吗?

顺便说一句,老实说,在我正在做的另一个项目中,我发现动态库和静态库中全局变量的地址是相同的。但是那个项目太大了,我无法提供一个小程序来重现这种行为。

非常感谢!

【问题讨论】:

    标签: c++ linux global-variables dynamic-library dlsym


    【解决方案1】:

    您展示的第一个命令构建了一个共享对象d.so。根据您问题的上下文,我推测您还打算与d.so 链接,但您的第二个命令似乎缺少该部分。我假设这是一个错字,因为这是您显示的程序输出的唯一解释——A.cpp 既直接链接到,也内置到您的 d.so 库中。

    鉴于此,引用您链接的文章:

    两者都使用的目标代码例程不应在每个中重复。 对于使用静态变量的代码尤其如此,例如 单例类。静态变量是全局的,因此只能是 代表一次。包含两次会产生意想不到的结果。

    但这正是您似乎违反的规则,您在 d.so 和主应用程序可执行文件中两次表示 A 类的静态范围实例。

    所以,这似乎是指示的结果:“意外结果”。

    【讨论】:

    • 感谢您的回答!你是对的。第一个 cmd 行生成一个d.so,但我认为我没有以错误的方式链接它。有两种方法可以链接动态库。一种是在编译程序时链接,另一种是使用dlopendlsymdlclose。 (linux.die.net/man/3/dlopen) 我用的是后一种,如图main.cpp。 (我的意思是这不是错字,cmd 行可以在我的机器上执行^_^)。但是……你确实让我想到dlopen 是否会导致目标代码例程的重复。
    • dlopen——当然。 dlopen 打开指定的共享库并将其映射到可执行文件的地址空间。 dlopen 绝对没有任何逻辑来确定可执行文件是否定义了与共享库相同的符号,并且以某种方式不从共享库中加载它们。要么加载整个库,要么不加载。
    • dlopen 确实在动态部分创建了全局变量的新副本,这与静态部分的不同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-25
    • 2022-11-07
    • 2022-01-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多