您的位置:首页 > 编程语言 > C语言/C++

C++调用C的库函数 undefined reference to

2012-03-23 14:07 344 查看
C++调用C的库函数时,如果头文件定义得不恰当,可能会出现明明某函数在obj文件中存在,但是却发生链接失败的情况,出现如下错误:

undefined reference to 'xxx'

出现问题的原因是c库函数编译成obj文件时对函数符号的处理和C++不同。因为C++函数支持重载,所以函数符号的处理要更复杂一些,c往往不作修饰。

例如有函数:

/* dofunc.c */

#include <stdio.h>

int dofunc()

{

printf("dofunc\n");

}

使用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[])

{

dofunc();

system("pause");

}

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 dofunc();

}

int main(int argc
, char* args[])

{

dofunc();

system("pause");

}

g++ -o main_dev main_dev.cpp dofunc.o

成功

extern "C"修饰内的函数,一律按照c的风格来编译,以便能够链接到用c编译出来的obj库上去。

常见有形如:

#ifdef __cplusplus

extern "C" {

#endif

int dofunc();

#ifdef __cplusplus

}

#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'

因为fofunc.o里面的符号是__Z6dofuncv
,所以链接会失败,只能有一种非常恶心的方法去链到那个函数:

//main_dev.c

int (*dofunc)(); /* 声明函数指针 */

int _Z6dofuncv();
/* 会链接到 __Z6dofuncv */

int main(int argc
, char* args[])

{

dofunc=_Z6dofuncv; /* 函数指针赋值 */

dofunc();

system("pause");

}

gcc -o main_dev main_dev.c dofunc.o

成功

上面讲了那么多,中心意思都是c和c++编译和链接时对函数名加工的细节问题,理解了这些细节后,如何运用完全就存乎一心了。

以上浅见,欢迎指正。

本文出自 “软件工匠笔记” 博客,请务必保留此出处http://linhs.blog.51cto.com/370259/140927
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐