转自: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\n");
}

使用gcc编译成obj后

gcc -c dofunc.c
#生成 dofunc.o objdump -x dofunc.o [ ](sec -)(fl 0x00)(ty )(scl ) (nx ) 0x00000000 dofunc.c
File
[ ](sec )(fl 0x00)(ty )(scl ) (nx ) 0x00000000 _dofunc
AUX tagndx ttlsiz 0x0 lnnos next
[ ](sec )(fl 0x00)(ty )(scl ) (nx ) 0x00000000 .text
AUX scnlen 0x14 nreloc nlnno
[ ](sec )(fl 0x00)(ty )(scl ) (nx ) 0x00000000 .data
AUX scnlen 0x0 nreloc nlnno
[ ](sec )(fl 0x00)(ty )(scl ) (nx ) 0x00000000 .bss
AUX scnlen 0x0 nreloc nlnno
[ ](sec )(fl 0x00)(ty )(scl ) (nx ) 0x00000000 .rdata
AUX scnlen 0x8 nreloc nlnno
[ ](sec )(fl 0x00)(ty )(scl ) (nx ) 0x00000000 _printf

c的dofunc函数在obj文件里的符号为 _dofunc
 
  再看看使用g++编译后的代码:

g++ -c dofunc.c

objdump -x dofunc.o

SYMBOL TABLE:
[ ](sec -)(fl 0x00)(ty )(scl ) (nx ) 0x00000000 dofunc.c
File
[ ](sec )(fl 0x00)(ty )(scl ) (nx ) 0x00000000 __Z6dofuncv
AUX tagndx ttlsiz 0x0 lnnos next
[ ](sec )(fl 0x00)(ty )(scl ) (nx ) 0x00000000 .text
AUX scnlen 0x14 nreloc nlnno
[ ](sec )(fl 0x00)(ty )(scl ) (nx ) 0x00000000 .data
AUX scnlen 0x0 nreloc nlnno
[ ](sec )(fl 0x00)(ty )(scl ) (nx ) 0x00000000 .bss
AUX scnlen 0x0 nreloc nlnno
[ ](sec )(fl 0x00)(ty )(scl ) (nx ) 0x00000000 .rdata
AUX scnlen 0x8 nreloc nlnno
[ ](sec )(fl 0x00)(ty )(scl ) (nx ) 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 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

用C++调用C的库函数(转载)的更多相关文章

  1. Linux:使用rpcgen实现64位程序调用32位库函数

    摘要:本文介绍使用rpcgent实现64位程序调用32位库函数的方法,并给出样例代码. 我的问题 我的程序运行在64位Linux系统上,需要使用一个从外部获得的共享库中的函数,这个共享库是32位的,无 ...

  2. Java调用MySql数据库函数

    Java调用MySql数据库函数 /** * 调用mysql的自定义函数 * */ private void test() { logger.info("show task start &q ...

  3. 用VC调用EXCEL简单代码(转载自越长大越孤单,觉得很好)

    首先在stdafx.h里加入对IDispatch接口提供支持的头文件: #include <afxDisp.h> 再在应用程序类的InitInstance()函数里加入: AfxOleIn ...

  4. 采用动态代理方式调用WEB服务(转载+整理)

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  5. 在Java中直接调用js代码(转载)

    http://blog.csdn.net/xzyxuanyuan/article/details/8062887 JDK1.6版添加了新的ScriptEngine类,允许用户直接执行js代码. 在Ja ...

  6. Jquery Ajax调用aspx页面方法 (转载)

    在asp.net webform开发中,用jQuery ajax传值一般有几种玩法 1)普通玩法:通过一般处理程序ashx进行处理: 2)高级玩法:通过aspx.cs中的静态方法+WebMethod进 ...

  7. LoadRunner调用Java程序—性能测试-转载

    LoadRunner调用Java程序—性能测试   为了充分利用LoadRunner的场景控制和分析器,帮助我们更好地控制脚本加载过程,从而展现更直观有效的场景分析图表.本次将重点讨论LoadRunn ...

  8. c# 异步调用简单例子(转载)

    首先来看一个简单的例子: 小明在烧水,等水烧开以后,将开水灌入热水瓶,然后开始整理家务 小文在烧水,在烧水的过程中整理家务,等水烧开以后,放下手中的家务活,将开水灌入热水瓶,然后继续整理家务 这也是日 ...

  9. Matlab以MEX方式调用C源代码【转载】

    原文地址:http://blog.sina.com.cn/s/blog_468651400100coas.html 这是自己整理的一个对应的文档:<Matlab以MEX方式调用C源代码> ...

随机推荐

  1. excel 分类汇总函数

    1.先用数组公式对单元格区域 B3:B39 ,进行提取去重复非空调单元格信息.单元格B52数组公式: =INDIRECT(TEXT(MIN(IF((COUNTIF(B$51:B51,B$3:B$39) ...

  2. scrollReveal 使用

    传统的layzload只能适用于图片懒加载,而我们现在需要的是全部元素的懒加载! 官网:https://scrollrevealjs.org/ gitHub:https://github.com/jl ...

  3. stl之multiset容器的应用

    与set集合容器一样,multiset多重集合容器也使用红黑树组织元素数据,仅仅是multiset容器同意将反复的元素健值插入.而set容器则不同意. set容器所使用的C++标准头文件set.事实上 ...

  4. linked-list-cycle——链表、判断是否循环链表、快慢指针

    Given a linked list, determine if it has a cycle in it. Follow up:Can you solve it without using ext ...

  5. poj 2154 Color 欧拉函数优化的ploya计数

    枚举位移肯定超时,对于一个位移i.我们须要的是它的循环个数,也就是gcd(i,n),gcd(i,n)个数肯定不会非常多,由于等价于n的约数的个数. 所以我们枚举n的约数.对于一个约数k,也就是循环个数 ...

  6. YII 多子域名同步登录

    a.meylou.com和b.meylou.com不做登录.c.meylou.com这个专门做用户登录.c站登录之后a,b站点同时登录. 第一步:修改php.ini配置文件,把cookie_domai ...

  7. 【c语言】二维数组中的查找,杨氏矩阵在一个二维数组中,每行都依照从左到右的递增的顺序排序,输入这种一个数组和一个数,推断数组中是否包括这个数

    // 二维数组中的查找,杨氏矩阵在一个二维数组中.每行都依照从左到右的递增的顺序排序. // 每列都依照从上到下递增的顺序排序.请完毕一个函数,输入这种一个数组和一个数.推断数组中是否包括这个数 #i ...

  8. 面向接口的webservice发布方式

    import javax.jws.WebService; /**面向接口的webservice发布方式 */ @WebService public interface JobService { pub ...

  9. angularjs中常见错误

    使用angularjs时间不是非常长,理解不够透彻.但为刚開始学习的人还是能够帮助点的. 1.回调函数. . ...-->切记它是异步的,出现莫名其妙的问题记得查看一下 2.内存泄露. .... ...

  10. 【网站支付PHP篇】thinkPHP集成汇潮支付(ecpss)

    系列目录 支付宝集成:http://www.cnblogs.com/nerve/p/3437879.html 系列说明 最近在帮朋友的系统安装支付模块(兑换网站积分),现在总结一些开发心得,希望对大家 ...