用C++调用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\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

时间: 2024-08-03 00:06:12

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

LINUX下c语言调用math.h库函数的注意事项

在Ubuntu上做C语言程序练习时,用到了sqrt函数,尽管在源文件中已包含了math.h头文件,但在编译的时候总是提示sqrt未定义.后来在网上查阅了相关资料,得知:在Linux系统下,C源文件若调用了math库里的函数,则编译时要加上-lm(是字母l,不是数字1),表示链接到math库.例如编译调用了math库函数的C源文件foo.c,可以用如下命令对其进行编译: $gcc -o foo -g -W foo.c -lm

C++、C#互调用之C++ 调用C# dll (转载)

1.c# 创建dll 建立C#编写的DLL程序AddDll,项目类型为:类库 程序代码: using System; using System.Collections.Generic; using System.Text; namespace AddDll { public class Add { public int iadd(int a, int b) { int c = a + b; return c; } } } 2.C++编写调用程序 建立C++的Win32控制台应用程序UseDll,

ISE 调用第三方文本编辑器---------转载

转载至:http://www.eefocus.com/zqlxidian/blog/13-05/293855_5fe95.html 一.使用UE. 这一段是转发,写的挺详细.因为在做大的工程的时候 ,显然再用ISE自带的编辑器就比较不方便了. 1.在ISE里面指定UE作为默认编辑器.edit---preferences打开属性配置,左侧点Editors,右侧editor选择ultraedit再点OK就可以了,不过有时候这样选择以后双击项目里面的vhdl文件并不能启动UE,如果出现这样的情况可以在

MFC调用C动态库函数-----待补充

界面用MFC写 后台用写好的C程序就行了 C写的程序编译为dll,把用到的功能做成导出函数,在C++ w/MFC程序中调用 1.VC可以编译C函数动态库: 2.简单的dll编写例子(以最简单的两数相加函数为例): 创建工程项目Win32 Dynamic-Link Library. 加入头文件及源文件比如dll.h dll.cpp, 在头文件中:#ifndef DLL_H#define DLL_Hextern "C" int __declspec(dllexport)add(int x,

matlab调用c程序(转载)

通过把耗时长的函数用c语言实现,并编译成mex函数可以加快执行速度. Matlab本身是不带c语言的编译器的,所以要求你的机器上已经安装有VC,BC或Watcom C中的一种. 如果你在安装Matlab时已经设置过编译器,那么现在你应该就可以使用mex命令来编译c语言的程序了. 如果当时没有选,就在Matlab里键入mex -setup,下面只要根据提示一步步设置就可以了. 为了测试你的路径设置正确与否,把下面的程序存为hello.c. /*hello.c*/ #include "mex.h&q

RPC调用框架比较分析--转载

原文地址:http://itindex.net/detail/52530-rpc-%E6%A1%86%E6%9E%B6-%E5%88%86%E6%9E%90 什么是RPC: RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议. 简言之,RPC使得程序能够像访问本地系统资源一样,去访问远端系统资源. 比较关键的一些方面包括,通讯协议,序列化,资源(接口)描述,服务框架,性能,语言支持

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

LoadRunner调用Java程序-性能测试 为了充分利用LoadRunner的场景控制和分析器,帮助我们更好地控制脚本加载过程,从而展现更直观有效的场景分析图表.本次将重点讨论LoadRunner如何调用Java测试代码,完成压力测试. 通常我们在执行一些Server的压力测试的时候,总会不经意间想要一个Client完成对Server的调用示例,以至于我们可以通过LoadRunner直接录制,对于测试人员来说确实很方便.不过,开发人 员如果没有那么多时间去为测试人员服务,那可能就比较悲剧了,

C#调用Oracle存储过程【转载】

1.创建表 create table test (ID number, NAME varchar2(10), SEX varchar2(4), AGE number, ADDRESS varchar2(200) ); 2.创建不带参数的存储过程 create or replace procedure proc1 is begin insert into test(ID,NAME,SEX,AGE) values (1,'moses','man',25); commit; end; / 3.写C#代

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

原文地址:http://blog.sina.com.cn/s/blog_468651400100coas.html 如果我有一个用C语言写的函数,实现了一个功能,如一个简单的函数: double add(double x, double y) { return x + y; } 现在我想要在Matlab中使用它,比如输入: >> a = add(1.1, 2.2) 3.3000 要得出以上的结果,那应该怎样做呢? 解决方法之一是要通过使用MEX文件,MEX文件使得调用C函数和调用Matlab的