C++调用C库函数详细讲解

    技术2022-05-20  28

     

    C++调用C库函数详细讲解 文章录入:7747.Net    责任编辑:7747.Net  165

    【字体:小 大 】

    C 调用C的库函数时,如果头文件定义得不恰当,可能会出现明明某函数在obj文件中存在,但是却发生链接失败的情况,出现如下错误:   undefined reference to 'xxx' 出现问题的原因是c库函数编译成obj文件时对函数符号的处理和C 不同。因为C 函数支持重载,所以函数符号的处理要更复杂一些,c往往不作修饰。 例如有函数: /* dofunc.c */ #include <stdio.h> int dofunc() {     printf("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[]) {     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库上去。   常见有形如: __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 编译和链接时对函数名加工的细节问题,理解了这些细节后,如何运用完全就存乎一心了。 #ifdef __cplusplus extern "C" { #endif int dofunc(); #ifdef __cplusplus } #endif 的头文件声明。   这种的头文件一般是库开发者提供的,能同时被c和c 模块使用。

     

     

     

    转自http://www.7747.net/kf/201009/74570.html


    最新回复(0)