c++呼叫c的庫函式時,如果標頭檔案定義得不恰當,可能會出現明明某函式在obj檔案中存在,但是卻發生鏈結失敗的情況,出現如下錯誤:
undefined reference to '***'
出現問題的原因是c庫函式編譯成obj檔案時對函式符號的處理和c++不同。因為c++函式支援過載,所以函式符號的處理要更複雜一些,c往往不作修飾。
例如有函式:
/* dofunc.c */
#include
int dofunc()
使用gcc編譯成obj後
gcc -c dofunc.c
#生成 dofunc.o
objdump -x dofunc.o
[ 0](sec -2)(fl 0x00)(ty 0)(scl 103) (nx 1) 0x00000000 dofunc.c
file
[ 2](sec 1)(fl 0x00)(ty 20)(scl 2) (nx 1) 0x00000000 _dofunc
aux tagndx 0 ttlsiz 0x0 lnnos 0 next 0
[ 4](sec 1)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .text
aux scnlen 0x14 nreloc 2 nlnno 0
[ 6](sec 2)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .data
aux scnlen 0x0 nreloc 0 nlnno 0
[ 8](sec 3)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .bss
aux scnlen 0x0 nreloc 0 nlnno 0
[ 10](sec 4)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .rdata
aux scnlen 0x8 nreloc 0 nlnno 0
[ 12](sec 0)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x00000000 _printf
c的dofunc函式在obj檔案裡的符號為 _dofunc
再看看使用g++編譯後的**:
g++ -c dofunc.c
objdump -x dofunc.o
symbol table:
[ 0](sec -2)(fl 0x00)(ty 0)(scl 103) (nx 1) 0x00000000 dofunc.c
file
[ 2](sec 1)(fl 0x00)(ty 20)(scl 2) (nx 1) 0x00000000 __z6dofuncv
aux tagndx 0 ttlsiz 0x0 lnnos 0 next 0
[ 4](sec 1)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .text
aux scnlen 0x14 nreloc 2 nlnno 0
[ 6](sec 2)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .data
aux scnlen 0x0 nreloc 0 nlnno 0
[ 8](sec 3)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .bss
aux scnlen 0x0 nreloc 0 nlnno 0
[ 10](sec 4)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .rdata
aux scnlen 0x8 nreloc 0 nlnno 0
[ 12](sec 0)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x00000000 _printf
g++編譯後的函式符號名比較古怪:__z6dofuncv
可見c和c++在加工函式名方面是很大不同的。
如果有c++程式要使用dofunc.o ,如下程式的函式宣告是錯的
// main_dev.cpp
int dofunc();
int main(int argc , char* args)
g++ -o main_dev main_dev.cpp dofunc.o
main_dev.cpp: undefined reference to `dofunc()'
collect2: ld returned 1 exit status
原因是dofunc函式在加工後函式名應該為__z6dofuncv ,dofunc.o檔案裡面的是_dofunc,所以找不到。
如果有dofunc的源**,解決辦法很簡單,將dofunc.c使用c++來編譯即可。
如果不幸地dofunc函式在別人的庫裡面,而這個庫是用c編寫和gcc編譯的,源**不可見,那怎麼辦呢?
幸虧c++和編譯器的設計者早已料到了這個問題,並提供了一種通用的解決辦法:使用extern "c"來修飾舊c庫的外部函式宣告。
extern "c"
int main(int argc , char* args)
g++ -o main_dev main_dev.cpp dofunc.o
成功extern "c"修飾內的函式,一律按照c的風格來編譯,以便能夠鏈結到用c編譯出來的obj庫上去。
常見有形如:
#ifdef __cplusplus
extern "c"
#endif
的標頭檔案宣告。
這種的標頭檔案一般是庫開發者提供的,能同時被c和c++模組使用。巨集__cplusplus 是c++編譯器定義的,這種寫法保證了用c++編譯時extern "c" 能生效;而用c編譯時又不會因不會處理extern "c"而錯誤。
反過來,如果c需要呼叫c++編譯的庫又怎麼辦呢?相信一般情況下不會有這樣奇特的要求,直接用c++編譯不就完了?
把main_dev.cpp改名為main.c ,然後
gcc -o main_dev main_dev.c dofunc.o
當然會出現: undefined reference to `dofunc'
//main_dev.c
int (*dofunc)(); /* 宣告函式指標 */
int _z6dofuncv(); /* 會鏈結到 __z6dofuncv */
int main(int argc , char* args)
gcc -o main_dev main_dev.c dofunc.o
成功上面講了那麼多,中心意思都是c和c++編譯和鏈結時對函式名加工的細節問題,理解了這些細節後,如何運用完全就存乎一心了。
以上淺見,歡迎指正。
C 呼叫C庫函式詳細講解
c 呼叫c庫函式詳細講解 2011年03月03日 c 呼叫c庫函式詳細講解 undefined reference to 出現問題的原因是c庫函式編譯成obj檔案時對函式符號的處理和c 不同。因為c 函式支援過載,所以函式符號的處理要更複雜一些,c往往不作修飾。例如有函式 dofunc.c incl...
C 動態呼叫共享庫函式
1.需求.我們需要呼叫乙個函式 std string getvalue int index 這個函式定義在庫t1.so裡面 2.t1.so cat t1.c include extern c std string getvalue int index cc g t1.c o t1.so 3.用法 c...
qt呼叫c語言函式庫 C 呼叫C語言的庫函式
在專案中,使用c語言編寫了乙個socket後台程式tkcofferd,並且為方便客戶端的使用,提供了動態庫,其中包含socket介面。現在的需求是使用qt做乙個前端介面,用來展示tkcofferd的socket介面功能,用於測試目的。qt中使用c 語言編寫,如果需要呼叫tkcofferd的socke...