用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 [ ](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的库函数(转载)的更多相关文章
- Linux:使用rpcgen实现64位程序调用32位库函数
摘要:本文介绍使用rpcgent实现64位程序调用32位库函数的方法,并给出样例代码. 我的问题 我的程序运行在64位Linux系统上,需要使用一个从外部获得的共享库中的函数,这个共享库是32位的,无 ...
- Java调用MySql数据库函数
Java调用MySql数据库函数 /** * 调用mysql的自定义函数 * */ private void test() { logger.info("show task start &q ...
- 用VC调用EXCEL简单代码(转载自越长大越孤单,觉得很好)
首先在stdafx.h里加入对IDispatch接口提供支持的头文件: #include <afxDisp.h> 再在应用程序类的InitInstance()函数里加入: AfxOleIn ...
- 采用动态代理方式调用WEB服务(转载+整理)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- 在Java中直接调用js代码(转载)
http://blog.csdn.net/xzyxuanyuan/article/details/8062887 JDK1.6版添加了新的ScriptEngine类,允许用户直接执行js代码. 在Ja ...
- Jquery Ajax调用aspx页面方法 (转载)
在asp.net webform开发中,用jQuery ajax传值一般有几种玩法 1)普通玩法:通过一般处理程序ashx进行处理: 2)高级玩法:通过aspx.cs中的静态方法+WebMethod进 ...
- LoadRunner调用Java程序—性能测试-转载
LoadRunner调用Java程序—性能测试 为了充分利用LoadRunner的场景控制和分析器,帮助我们更好地控制脚本加载过程,从而展现更直观有效的场景分析图表.本次将重点讨论LoadRunn ...
- c# 异步调用简单例子(转载)
首先来看一个简单的例子: 小明在烧水,等水烧开以后,将开水灌入热水瓶,然后开始整理家务 小文在烧水,在烧水的过程中整理家务,等水烧开以后,放下手中的家务活,将开水灌入热水瓶,然后继续整理家务 这也是日 ...
- Matlab以MEX方式调用C源代码【转载】
原文地址:http://blog.sina.com.cn/s/blog_468651400100coas.html 这是自己整理的一个对应的文档:<Matlab以MEX方式调用C源代码> ...
随机推荐
- 协议的注冊与维护——ndpi源代码分析
在前面的文章中,我们对ndpi中的example做了源代码分析.这一次我们将尽可能深入的了解ndpi内部的结构和运作.我们将带着以下三个目的(问题)去阅读ndpi的源代码. 1.ndpi内部是怎么样注 ...
- 【转载】细聊分布式ID生成方法
一.需求缘起 几乎所有的业务系统,都有生成一个记录标识的需求,例如: (1)消息标识:message-id (2)订单标识:order-id (3)帖子标识:tiezi-id 这个记录标识往往就是数据 ...
- Linux bridge-utils tunctl 使用
网络 brctl是Linux下用来管理以太网桥.在内核中建立.维护.检查网桥配置的命令 STP - Spanning Tree Protocol(生成树协议)逻辑上断开环路.防止二层网络的广播风暴的产 ...
- MAC Intellij IDEA 经常使用快捷键(本人亲自測试过)
MAC Intellij IDEA 经常使用快捷键 keymap 设置 MAC OS X 10.5+ alt+f7查找变量方法使用的地方 F3加入书签 Ctrl + O快捷覆写方法 Alt + F3 ...
- mvn -v 报错解决办法
由于近期公司需求,我找到了个maven教程:http://wentao365.iteye.com/blog/903396 安装maven其实很简单,就是在Apache官网下载需要的maven包,然后配 ...
- 【特征检測】BRIEF特征点描写叙述算法
简单介绍 BRIEF是2010年的一篇名为<BRIEF:Binary Robust Independent Elementary Features>的文章中提出,BRIEF是对已检測到的特 ...
- querying rpm database
Call dbMatch on a transaction set to create a match iterator. As with the C API, a match iterator al ...
- poj 1742 Coins(二进制拆分+bitset优化多重背包)
\(Coins\) \(solution:\) 这道题很短,开门见山,很明显的告诉了读者这是一道多重背包.但是这道题的数据范围很不友好,它不允许我们直接将这一题当做01背包去做.于是我们得想一想优化. ...
- 文件批量转换成UTf-8
yum install -y enca 在文件夹根目录下面创建文件:iconv_shell.sh 里面填写下面的内容: #!/bin/bash for file in `find ./ -name ' ...
- 在centos7下手工安装和配置Nginx
一.什么是Nginx Nginx("enginex")是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP代理服务器,在高连接并发的情况下Nginx是Apa ...