C/C++代码的混合使用
一、extern "C"
C/C++的代码要混合使用,肯定要用到extern "C"了。extern "C"暗示编译器,被它修饰的内容使用C的链接风格,什么意思呢,直接上代码:
1 //sample.cpp 2 3 void fun(int a) 4 { 5 } 6 7 void fun(int a, double b) 8 { 9 } 10 11 extern "C" void foo(int a) 12 { 13 }
这里有三个函数,首先是重载的两个fun函数,然后是用extern "C"修饰的foo函数。用g++编译一下,然后查看下导出的符号:
发现了吧,两个fun函数编译后导出的符号是前缀"_Z3"加上函数名,再依次加上参数类型的简写。所以void fun(int a)变成了_Z3funi,void fun(int a, double b)变成了_Z3funid。这样做是为了支持函数重载,即,只要参数不同就可以认为是不同的函数。而用extern "C"修饰的函数foo,编译后导出的符号仅仅只是函数名。这就是C的链接风格,当然这样函数重载也就没办法支持了。
用了extern "C",使得无论是C还是C++的代码,编译过后针对同一个函数使用相同的符号,这样链接到一起的时候就不会报“undefined reference"了。实际使用时有两种场合,要么是C++代码调用C代码,要么是C代码调用C++代码。
顺便提一下,extern "C"只能写在C++代码中,C编译器是不认的。
二、C++调用C
还是直接看示例代码清晰明了:
1 //caller.cpp 2 3 #include <iostream> 4 5 using namespace std; 6 7 extern "C" void fun(void); 8 9 int main() 10 { 11 cout << "I'm caller.." << endl; 12 fun(); 13 }
1 //fun.c 2 3 #include <stdio.h> 4 5 void fun(void) 6 { 7 printf("This is C function \"fun()\"\n"); 8 }
这样两个文件,简单编译运行一下:
当然,如果把extern "C" void fun(void);放到头文件中风格会更好。本人比较懒,这里的简单示例就不按规矩写了。
三、C调用C++
这部分写了两个例子,首先是调用C++的普通函数,然后是调用类成员函数。
同样看代码,不过,先看一下被调用者C++代码:
1 //fun.cpp 2 3 #include <iostream> 4 using namespace std; 5 6 void fun(int a) 7 { 8 cout << "This is cpp function \"fun(" << a << ")\"" << endl; 9 } 10 11 void fun(int a, double b) 12 { 13 cout << "This is cpp function \"fun(" << a << ", " << b << ")\"" << endl; 14 } 15 16 extern "C" void foo(int a) 17 { 18 cout << "This is cpp function \"foo(" << a << ")\"" << endl; 19 } 20 21 extern "C" { 22 23 void fun_i(int a) 24 { 25 fun(a); 26 } 27 28 void fun_d(int a, double b) 29 { 30 fun(a, b); 31 } 32 33 }
这里有5个函数,两个重载的fun函数,剩下三个函数foo, fun_i, fun_d全都用extern "C"修饰了,导出给C代码调用。关于extern "C"的两种写法,一目了然,我就不解释了。这里的fun_i和fun_d只是把fun的两个重载版本分别包装了一下,因为C是没有函数重载的概念的。foo没有重载,所以也没有包装函数。事实上,如果知道C++编译器命名符号的规则(本文一开始已讨论过),不用extern "C",不用包装函数,直接使用符号调用也是可以的。这种方式在下面caller.c中也有示范:
1 //caller.c 2 3 #include <stdio.h> 4 5 int main(int argc, const char *argv[]) 6 { 7 printf("I'm C caller...\n"); 8 9 fun_i(1); 10 fun_d(2, 2.1); 11 foo(3); 12 13 _Z3funi(4); 14 _Z3funid(5, 5.1); 15 16 return 0; 17 }
编译运行:
可见,用包装函数调用和用符号调用都可以工作。我这里链接了libstdc++库,因为我是用gcc编译的,如果用的是g++,libstdc++是会默认链接的。这就涉及到项目主体是C还是C++的问题了,如果主体是C代码,必须用gcc编译,但是又引用了C++写的库,这时候链接一定要加上libstdc++。反之,如果主体是C++,用g++编译不会有类似的问题。
下面再提供一个用C代码调用C++类成员函数的例子,用包装函数:
1 //fun.cpp 2 3 #include <iostream> 4 using namespace std; 5 6 class shit 7 { 8 public: 9 shit(){} 10 ~shit(){} 11 void happen(); 12 }; 13 14 void shit::happen() 15 { 16 cout << "Holy shit!!" << endl; 17 } 18 19 extern "C" 20 void run() 21 { 22 shit fresh_shit; 23 fresh_shit.happen(); 24 }
1 //caller.c 2 3 #include <stdio.h> 4 5 int main(int argc, const char *argv[]) 6 { 7 printf("I'm C caller...\n"); 8 run(); 9 10 return 0; 11 }
编译运行:
转载于:https://www.cnblogs.com/suosuopuo/p/3388748.html
C/C++代码的混合使用相关推荐
- Applet中签名与未签名代码的混合使用带来的问题
Java Applet如果需要访问本地的受限资源,需要对访问的代码进行数字签名才能进行.Java SE 19 release或者以后的版本为了Applet的安全性,当所执行的Applet代码既包括已签 ...
- svn如何隐藏代码路径_程序员课堂—如何通过改善代码风格来消灭隐藏bug
写在前面:一名有三年Android开发经验的女程序员(欢迎大家关注我 ~期待和大家一起交流和学习Android的相关知识) 正如食物腐烂之前,可能会发出异味.当代码存在隐藏问题时,代码也会表现出一些异 ...
- 老码农绝密:使用 TS(TypeScript) 的 10 大理由
最近,小编读了一篇名为<放弃 TypeScript 的 7 个非常好的理由>,这篇文章的阅读量不低.里面有些观点确实有趣,不过在这里我要向你介绍使用 TypeScript 的 10 个理由 ...
- python输入语句-python输入语句
广告关闭 2017年12月,云+社区对外发布,从最开始的技术博客到现在拥有多个社区产品.未来,我们一起乘风破浪,创造无限可能. python条件语句目录:1. 分支语句(if...else...)2. ...
- python与excel表格-超简单:用Python让Excel飞起来
超简单:用Python让Excel飞起来 作者:王秀文;郭明鑫;王宇韬 编著 出版日期:2020年07月 文件大小:20.30M 支持设备: ¥45.00在线试读 适用客户端: 言商书局 iPad/i ...
- 【eoe教程】Android中自定义视图的绘制方法
原文链接 :http://android.eoe.cn/topic/ui 自定义视图最重要的部分是它的外观.你可以根据应用的需求简单或复杂的实现它. 这个教程包含了最常见的操作. 重写onDraw() ...
- 欢迎参与Java 事务讨论
欢迎参与Java 事务讨论 bruce http://www.jdon.com Jul 14, 2003 6:13 AM 回复 ***************************** **JTA与 ...
- python怎么判断是不是汉字危机_谈 Python 的中文编码处理
最近业务中需要用 Python 写一些脚本.尽管脚本的交互只是命令行 + 日志输出,但是为了让界面友好些,我还是决定用中文输出日志信息. 很快,我就遇到了异常: Python代码 UnicodeE ...
- python打造excel神器_超简单:用Python让Excel飞起来
前言 如何获取学习资源 章Python快速上手 1.1为什么要学习用Python控制Excel 1.2Python编程环境的搭建 1.2.1安装Python官方的编程环境IDLE 1.2.2安装与配置 ...
最新文章
- Build-dep linux 知乎,删除通过apt-get build-dep安装的软件包
- 12.10 Nginx访问日志 12.11 Nginx日志切割 12.12 静态文件不记录日志和过期时间
- Java web(2012/2/23)
- wpf mvvm框架_[Windows] 在 Microsoft Docs 网站中挖掘 MVVM 的各种学习资源
- html表格宽度拖拽,原生js实现 拖拽改变 table表格列宽
- StretchDIBits函数隐含的图像坐标系设置
- 出身平凡的郭盛华,究竟是如何逆袭成为传奇人物的?
- C语言半框,如何挑选适合自己的镜框(镜片)?
- 玩游戏用云电脑选高配有延迟吗
- 一款兼容Arduino的模拟TDS传感器
- iphone视频照片恢复
- 创建OMF(Oracle Managed Files,Oracle管理的文件)
- 当你凝视深渊时,深渊也在凝视着你。
- PHP是TM最美的语言,我的文章是最棒的狗粮
- Ceph Cache Tier
- Linux下shell脚本的4中执行方式
- 2016年度顶级开源创作工具
- 五问目标检测Anchor
- 糖尿病遗传风险检测挑战赛
- 2.3 黑群晖驱动:开启nvme缓存、将nvme缓存作为存储盘 教程
热门文章
- 下列符合c语言语法的字符常量是,C语言程序设计(周经亚)选择题练习-chapter 5.doc...
- sierra mysql_macOS High Sierra 使用 Homebrew 安装 MYSQL 5.7
- 数学之美系列之一:统计语言模型 (Statistical Language Models)
- 零件分组pascal程序
- BZOJ 4327 [JSOI2012]玄武密码 (AC自动机)
- redhat linux下安装oracle10g rac,RedHat 5.5下安装Oracle 10g+RAC
- xftp传输文件报错_Xshell+Xftp--Windows平台远程终端解决方案
- 用C语言编写小学四则运算程序,用C语言编写生成小学四则运算程序
- Bootstrap-Table入门篇
- Redis源码解析:14Redis服务器与客户端间的交互