轉 在C 編譯器下dlsym 引發的思考

2021-07-09 09:54:35 字數 3778 閱讀 5580

標籤: 編譯器

c++makefile

struct

cfunction

2010-07-01 17:54

1875人閱讀收藏 

舉報

技術(21)

cafesun 2007-02-16

這幾天看到講解dlopen,dlsym函式的文章,忍不住自己編碼嘗試了一下。引出了一些其他知識。

dlsym()的函式原型是

void* dlsym(void* handle,const char* symbol)

handle是由dlopen開啟動態鏈結庫後返回的指標,symbol就是要求獲取的函式的名稱,函式返回值是void*,指向函式的位址,供呼叫使用。dlsym的返回值與symbol引數就是本文著重要講述的要點。

先看下面一段**

dlltest.cpp//

#include "datetime.h"

#include

#include

using namespace std;

typedef int(*funcdateptr)(datetype* d);

int main(int argc,char* argv)

datetype d;

//timetype t;

void* dp=0;

char* error=0;

cout<

if(dp==0)

cout<

//int(*f)(datetype* d);

funcdateptr f=(funcdateptr)dlsym(dp,"getdate2");

//void* test=dlsym(dp,"getdate");

//funcdate=(funcdate)funcdate;

error=dlerror();

if(error)

cout<

f(&d);

cout<

dlclose(dp);

return 0;

#ifndef datetime_h_

#define datetime_h_

#ifdef __cplusplus

extern "c"{

#endif

struct datetype

int year;

int month;

int day;

struct timetype

int hour;

int minute;

int second;

int getdate2(datetype* d);

int gettime2(timetype* t);

#ifdef __cplusplus

#endif

/#include

#include

#include "datetime.h"

using namespace std;

int getdate2(datetype* d)

long ti;

struct tm *tm;

time(&ti);

tm=localtime(&ti);

d->year=tm->tm_year+1900;

d->month=tm->tm_mon+1;

d->day=tm->tm_mday;

cout<

int gettime2(timetype* t)

long ti;

struct tm *tm;

time(&ti);

tm=localtime(&ti);

t->hour=tm->tm_hour;

t->minute=tm->tm_min;

t->second=tm->tm_sec;

return 0;

datetime.h,datetime.cpp兩個檔案主要包含兩個函式getdate2,gettime2(為什麼函式名如此,在下面會專門提到)生成了動態鏈結庫libtime.so。dlltest.cpp中的邏輯就是開啟libtime.so這個檔案,然後取getdate2函式,並呼叫它。

第乙個要講的就是 funcdateptr f=(funcdateptr)dlsym(dp,"getdate2");這個地方。看似簡單的乙個指標強制轉換,實際上並不是那麼簡單,這裡的指標轉換不同於一般的指標轉換,實際上是將乙個指向物件的指標轉換為指向函式的指標,這裡除了使用傳統的強制轉換方式,還可以使用c++自帶的reinterpret_cast轉換符,可以這樣寫:

void* vf=dlsym(dp,"getdate2");

funcdateptr f=reinterpret_cast(vf);

這個轉換只在c++中需要,因而也只在c++中遇到這個問題。

第二點要講的是datetime.h中的

#ifdef __cplusplus

extern "c"{

#endif

......

#ifdef __cplusplus

#endif

為什麼一定要定義這樣的巨集呢,如果不定義會怎麼樣,也許很多人(包括我以前)也會這樣問。既然有這樣的疑問,那一切以例項說話,我去掉這個地方extern 「c」的宣告,看看編譯的程式執行起來會怎麼樣......

稍作修改後,dlltest依然可以編譯,無甚特別的。但執行一下呢?喔喔,怎麼老是報錯「error:no error」?error是我**裡面定義的輸出,那個no error是dlerror返回的字串。說是no error,其實還是有錯的,只是錯不在dlsym(),而是錯在我這裡。

可以返回去重新看看那段**

funcdateptr f=(funcdateptr)dlsym(dp,"getdate2");

如果你一眼看出問題了,那下面要講的內容,你可以不看了。如果3分鐘內沒有發現問題,那麼還是聽我羅嗦一下。大家肯定聽說過c++有name mangling機制吧(沒有聽說的話,可以檢視一下lippman的《深入探索c++物件模型》)。當我去掉了extern 「c」的時候,就注定會跌入這個陷阱。我用的g++編譯的庫檔案,g++自然的運用了name mangling技術,原本的getdate(datetype& d)函式,很有可能其名字已變成了_getdate_datetype(datetype& d)這樣(不同的編譯器實現不一樣),而對於引用dlsym這樣的純c編譯好的庫中的函式,他們對name mangling機制無甚了解,不會有智慧型的轉換,所以它在c++編譯器下不會自動的去作名字處理,當我傳入getdate2作為symbol引數的時候,dlsym自然是找不到對應的函式位址的。但他自己又沒啥錯,就丟給我一串「no error」。

到了這裡,了解沒有extern 「c」會發生什麼事情,並且了解了原因後,我們可以轉回來。extern 「c」起了什麼作用,大家心裡應該都清楚了。我懶得引用官方正式的解釋了,雖然定義的很完善,但我覺得很撓口,很難懂。所以我把我自己的理解帖出來,不怕大家見笑。

extern 「c「就是告訴c++編譯器,到了這裡要用c編譯器的方式來編譯,不要什麼name mangling之類的高科技了。細緻的從語法上分析,有兩層含義,但同樣講得也是這個意思。

extern 關鍵字宣告被修飾的物件可供其他模組呼叫(這裡亂蓋一下,可能被亂磚砸暈。我試著用gcc編譯了一下,在c編譯器下,如果函式被extern修飾,其他模組引用該函式,可以不包含該模組的標頭檔案的)

「c」,告訴c++編譯器,這裡用c的標準編譯。

好了,dlsym引出的問題一一解決了,這裡提醒各位,在編譯的時候,最好加上-ldl引數。至於makefile,我想大家都有(或者不需要makefile),編譯的問題,我就不羅嗦了。over!

頂 0 踩

solaris下的C編譯器

想簡單開發,少不了編譯器。沒辦法,上網搜尋了一下,大家都在說sun studio12,看了一下介面也挺漂亮的。雖然挺大的,安裝上去還得1g左右,還是下了下來。結果一堆得問題。首先就是,提示臨時資料夾空間不夠,剛開始沒好好看提示。還理解錯誤了。最後用.sunstudio12u1 sunos x86 p...

c 編譯器編譯過程

編譯過程 編譯過程分為四步 預處理 編譯 彙編 連線 1.預處理 預處理主要處理原始檔中的 include define 等預處理等命令 預處理主要完成的工作有 參考 程式設計師的自我修養 1 刪除 define,展開巨集 2 處理條件編譯指令。預處理程式先判斷條件,再根據條件修改源 3 刪除注釋 ...

Linux下gcc編譯器和g 編譯器的那些事兒

使用c c 程式設計大約有三四個年頭了。最開始涉及到微控制器 嵌入式linux等,都使用的是c語言,那時主要寫linux驅動,甚至在arm板上寫linux應用程式時需要應用物件導向的思想的時候,都是使用c語言的結構體和函式指標來實現。當然,使用的編譯器自然就是gcc了。後來,慢慢的轉向了使用c 編寫...