2015-11-10 145 views
1

我在CentOS 6.5上遇到過。正如我在網上搜索的那樣,當使用動態庫時,靜態變量在Windows和Linux上的行爲不同。也就是說,Windows會導致變量的重複,Linux不會像這樣:但是,當我編寫一個小程序來驗證這一點時,我發現Linux也會導致重複。這是我的小程序,包括四個文件:靜態變量和全局變量在Linux上的動態庫和靜態庫中顯示不同的地址?

(1)阿

#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。任何人都請回答我的問題,或幫助找出程序中的一些錯誤(如果有的話)?順便說一下,說實話,在我正在開發的另一個項目中,我發現全局變量的地址在動態庫和靜態庫中是相同的。但是這個項目太大了,我無法提供一個小程序來重現這種行爲。

非常感謝!

回答

0

您顯示的第一條命令會生成一個共享對象d.so。根據你的問題的背景,我推測你也打算鏈接d.so,但你的第二個命令似乎缺少那部分。我假設它是一個錯字,因爲這是您顯示的程序輸出的唯一解釋 - A.cpp既直接鏈接,也內置於您的d.so庫中。

鑑於這種情況,從你鏈接的文章引用:

雙方使用的對象代碼例程不應該在每次重複。 對於使用靜態變量(如 單例類)的代碼尤其如此。一個靜態變量是全局的,因此只能代表一次 。包括它兩次將提供意想不到的結果。

但是,這正是你似乎被打破,你代表的A類的靜態範圍的情況下的兩倍,在你d.so,並在主應用程序的可執行規則。

所以,這似乎是顯示的結果:「意想不到的結果」。

+0

感謝您的回答!你是對的。第一個cmd行生成一個'd.so',但我認爲我沒有把它鏈接到一個錯誤的方式。有兩種方法可以鏈接一個動態庫。一種是在編譯程序時連接,另一種是使用'dlopen','dlsym'和'dlclose'。 (http://linux.die.net/man/3/dlopen)我使用後者,如'main.cpp'所示。 (我的意思是這不是一個錯字,cmd行可以在我的機器上執行^ _ ^)。但是......你確實讓我想到'dlopen'是否會導致目標代碼例程的重複。 –

+0

dlopen - 絕對。 dlopen打開指定的共享庫並將其映射到可執行文件的地址空間中。 dlopen絕對沒有任何邏輯來確定可執行文件是否定義了與共享庫相同的符號,並且不知何故不會從共享庫中加載它們。整個庫都被加載,或者不是。 –

+0

'dlopen'確實在動態部分創建了全局變量的新副本,這與靜態部分不同。 –