【VS开发】C中调用C++文件中定义的function函数

标签(空格分隔): 【VS开发】


声明:引用请注明出处http://blog.csdn.net/lg1259156776/


精要一揽

C调用C++,使用extern “C”则是告诉编译器依照C的方式来编译封装接口,当然接口函数里面的C++语法还是按C++方式编译。

使用extern “C” 主要是因为C编译器编译函数时不带参数的类型信息,只包含函数的符号名字。如

int foo( float x )

C编译器会将此函数编译成类似_foo的符号,C连接器只要找到了调用函数的符号,就认为连接成功。

而C++编译器为了实现函数重载,会在编译时带上函数的参数信息。如它可以把上面的函数编译成类似于foo_float这样的符号。

源文件在编译器下生成的目标文件中导出符号的规则问题

另外,具体的关于源文件在编译器下生成的目标文件中导出符号的规则问题,可以参看我的另一篇博文《[【读书笔记】程序员的自我修养总结(三)][2]》,具体的内容我粘贴如下:

由于全局符号在链接过程是全局可见的,所以如果编写的库文件中和当前的目标文件中有相同的符号名,那么就会发生冲突。为了防止类似的符号名冲突,UNIX下的C语言规定,C语言源代码文件中的所有全局变量和函数经过编译以后,相对应的符号名前面加上下划线,“_”。但如果是一个大型的软件,由不同的部门来开发,他们之间的命名规范如果不严格,则可能导致冲突,于是C++这样的语言使用了Namespace来解决不同模块下的符号冲突。

C++的符号修饰机制指的是C++语言支持不同参数类型的函数拥有一样的名字,即函数重载。实际上他们在编译的时候会进行一个函数签名,包含函数名,参数类型,所在类和命名空间等信息。

而名称修饰机制,也被用来防止静态变量,不同的编译器可能名称修饰方法不同,函数签名可能对应不同的修饰名称,由于不同的编译器采用不同的名字修饰方法,必然导致由不同编译器编译产生的目标文件无法正常相互链接。

“extern C”用法

C++中为了与C兼容,在符号管理上有一个用来声明或定义一个C的符号的“extern C”关键字用法:

extern “C”{
int func(int);
int var;
}

C++ 编译器会将extern “C”的大括号内部的代码当作C语言来处理。VC++平台会将C语言的符号进行修饰,即大括号中的func和var修饰后的符号为_func和_var,而C++部分的则按照C++的那一套进行修饰。

而下面的一段代码常常用来解决C/C++两种源码编译形式:

#ifdef __cplusplus
extern "C"{
#endif void *memset(void *, int, size_t); #ifdef __cplusplus
}
#endif

如果当前参与编译的是C++代码,memset会在extern “C”中被声明,按照C代码进行符号修饰;而如果是C代码,直接声明即可。(C语言不支持extern “C”,而__cplusplus这个宏是C++编译器默认定义的,如果是C++编译器参与的编译,则就默认定义了该宏。)。这段代码几乎在所有的系统头文件中都被利用。

C调用C++库

调用C++函数库,一般不能直接调用,需要将C++库转换成C接口输出,方可以使用C调用

将 C++ 函数声明为“extern “C””(在你的 C++ 代码里做这个声明),然后调用它(在你的 C 或者 C++ 代码里调用)。例如:

// C++ code:
extern "C" void f(int);
void f(int i)
{
// ...
}

然后,你可以在C文件中这样使用 f():

/* C code: */
void f(int);
void cc(int i)
{
f(i);
/* ... */
}

当然,这招只适用于非成员函数。如果你想要在 C 里调用成员函数(包括虚函数),则需要提供一个简单的包装(wrapper)。例如:

// C++ code:
class C
{
// ...
virtual double f(int);
}; extern "C" double call_C_f(C* p, int i) // wrapper function
{
return p->f(i);
}

然后,你就可以这样调用 C::f():

/* C code: */
double call_C_f(struct C* p, int i); void ccc(struct C* p, int i)
{
double d = call_C_f(p,i);
/* ... */
}

参数struct C* p从哪里来,即怎么在C中定义C++对象,其实上面只是说了思想,真实的c中使用C++类需要把原来的类都封装一下,参看下面的文章:

如何用C语言封装 C++的类,在 C里面使用

如果你想在 C 里调用重载函数,则必须提供不同名字的包装,这样才能被 C 代码调用。例如:

// C++ code:
void f(int);
void f(double); extern "C" void f_i(int i) { f(i); }
extern "C" void f_d(double d) { f(d); }

为什么这样做的目的很明显,因为C++为了实现复杂的重载函数功能,在目标文件生成的过程中,会将函数名与参数返回值等类型一起导出到函数符号中去,从而实现的重载函数之间的区分,所以声明为C的时候,必须要提供不同的函数名才行。

然后,你可以这样使用每个重载的 f():

/* C code: */

void f_i(int);
void f_d(double); void cccc(int i,double d)
{
f_i(i);
f_d(d);
/* ... */
}

注意,这些技巧也适用于在 C 里调用 C++ 类库,即使你不能(或者不想)修改 C++ 头文件。


2015-12-04 调试记录 张朋艺

VS开发】C中调用C++文件中定义的function函数的更多相关文章

  1. CUDA常见问题之无法在c文件中调用cu文件中定义的函数

    当在C源文件中调用cu文件中定义的函数时,会出现undefined reference的问题,而在C++源文件中调用cu文件中定义的函数时则不会出现这个问题. 出现上述问题的原因是,nvcc编译器采用 ...

  2. Delphi 中调用JS文件中的方法

    unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms ...

  3. C++学习——在C文件中调用C++文件中的函数

    1.CPP文件中的内容 #include "mytest.h" #include <iostream> using namespace std; int add(con ...

  4. C# winForm中调用javascript文件中的方法

    目前有很多的SNS社区或类SNS的网站,例如开心.51.校内等,但是发现大多数社区在邀请好友的时候都没有提供对QQ邮箱或者QQ空间好友列表获取的功能,不过似乎海内支持,但是网上相关QQ的文章还不是很多 ...

  5. java中调用dll文件的两种方法

    一中是用JNA方法,另外是用JNative方法,两种都是转载来的, JNA地址:http://blog.csdn.net/shendl/article/details/3589676   JNativ ...

  6. Native Application 开发详解(直接在程序中调用 ntdll.dll 中的 Native API,有内存小、速度快、安全、API丰富等8大优点)

    文章目录:                   1. 引子: 2. Native Application Demo 展示: 3. Native Application 简介: 4. Native Ap ...

  7. 在winform中调用js文件并输出结果

    在winform中调用js文件并输出结果默认分类 2007-10-19 16:35:06 阅读25 评论0 字号:大中小 由于项目需要在winform中调一个强大的js,所以把这个tip记录在此: 1 ...

  8. 在ASP.NET MVC 框架中调用 html文件及解析get请求中的参数值

    在ASP.NET MVC 框架中调用 html文件: public ActionResult Index() { using (StreamReader sr = new StreamReader(P ...

  9. Python 调用JS文件中的函数

    Python 调用JS文件中的函数 1.安装PyExecJS第三方库 2.导入库:import execjs 3.调用JS文件中的方法 Passwd = execjs.compile(open(r&q ...

随机推荐

  1. [2019HDU多校第一场][HDU 6580][C. Milk]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6580 题目大意:\(n\times m\)大小的方格上有\(k\)瓶水,喝完每瓶水都需要一定的时间.初 ...

  2. js实现上传文件夹

    上传大文件的解决方案 需求:项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在500M内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以501M来进行限制. 第一步: 前端 ...

  3. 7月清北学堂培训 Day 2

    今天是林永迪老师的讲授~ 继续昨日的贪心内容. 我们继续看例题: 分析样例的过河方法: 首先1和2先过河,总时间为2: 然后1回来,总时间为3: 然后5和10过河,总时间为13: 然后2回来,总时间为 ...

  4. MySQL数据分析—(4)关系数据库的三个逻辑框架

    (一)前面课程逻辑梳理 任何一门数据的软件也好,数据构架也好,或者说是数据学科也好,最终都是要解决实际问题的,大家说是不是? 前面jacky讲为什么要引入数据库的时候,举了一个案例,大家还记的吗?大家 ...

  5. vmware压缩vmdk文件大小

    在搭建靶机环境的过程中总是遇见vmdk越来越大,导致上传时间变长.记一下压缩vmdk的方法 ;sync;rm -f zero.fill /usr/bin/vmware-toolbox-cmd disk ...

  6. 第04组 Beta冲刺(1)

    队名:斗地组 组长博客:地址 作业博客:Beta冲刺(1/4) 各组员情况 林涛(组长) 过去两天完成了哪些任务: 1.分配展示任务 2.收集各个组员的进度 3.写博客 展示GitHub当日代码/文档 ...

  7. Node.js中npm常用命令大全

    npm是什么 NPM的全称是Node Package Manager,是随同NodeJS一起安装的包管理和分发工具,它很方便让JavaScript开发者下载.安装.上传以及管理已经安装的包. npm ...

  8. FreeMarker学习(springmvc配置)

    springMvc配置 <bean id="freemarkerConfig" class="org.springframework.web.servlet.vie ...

  9. Python JSON dump ,load,dumps,loads

    JSON是一种轻量级的数据交换格式. json.dump() 将Python数据格式序列化为json数据格式(字符串)并储存在json文件之中. json.load()将Jons数据(字符串)反序列化 ...

  10. C++ remove remove_if erase

    #include <iostream>#include <algorithm>#include <list>#include <vector>#incl ...