zoukankan      html  css  js  c++  java
  • C++调用C语言的库函数

    在项目中,使用C语言编写了一个socket后台程序tkcofferd,并且为方便客户端的使用,提供了动态库,其中包含socket接口。

    现在的需求是使用qt做一个前端界面,用来展示tkcofferd的socket接口功能,用于测试目的。

    qt中使用c++语言编写,如果需要调用tkcofferd的socket接口(由C语言编写),需要指明函数导出方式,详述如下:

    转自http://linhs.blog.51cto.com/370259/140927

     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库的外部函数声明。   

    1 extern "C" {
    2 int dofunc();
    3 }
    4 
    5 int main(int argc , char* args[])
    6 {
    7         dofunc();
    8         system("pause");
    9 }
    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  ,所以链接会失败,只能有一种非常恶心的方法去链到那个函数:
     

     1 //main_dev.c
     2 int (*dofunc)();    /* 声明函数指针   */
     3 int _Z6dofuncv();  /* 会链接到   __Z6dofuncv   */
     4 
     5 int main(int argc , char* args[])
     6 {
     7     dofunc=_Z6dofuncv;  /* 函数指针赋值 */
     8     dofunc();
     9     system("pause");
    10 }
    gcc  -o main_dev main_dev.c dofunc.o
    成功

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

  • 相关阅读:
    Kafka学习笔记之kafka高版本Client连接0.9Server引发的血案排查
    机器学习笔记之python实现朴素贝叶斯算法样例
    机器学习笔记之python实现支持向量机SVM算法样例
    机器学习笔记之AdaBoost算法详解以及代码实现
    机器学习笔记之python实现关联规则算法Apriori样例
    机器学习笔记之python实现AdaBoost算法
    F-47(copy 邓大顾)
    js 设置标题 空白
    微信授权验证
    iphone web 时间 问题
  • 原文地址:https://www.cnblogs.com/qxxnxxFight/p/4086596.html
Copyright © 2011-2022 走看看