【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. CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths (dsu on tree) 题解

    先说一下dsu算法. 例题:子树众数问题. 给出一棵树,每个点有点权,求每个子树中出现次数最多的数的出现次数. 树的节点数为n,\(n \leq 500000\) 这个数据范围,\(O(n \sqrt ...

  2. request.getParameter乱码

    String str= new String(request.getParameter("xxxx").getBytes("ISO-8859-1")," ...

  3. 让JPA的Query查询接口返回Map对象

    在JPA 2.0 中我们可以使用entityManager.createNativeQuery()来执行原生的SQL语句. 但当我们查询结果没有对应实体类时,query.getResultList() ...

  4. 数据分析九:互联网征信中的信用评分模型(用户APP使用行为分析)

    用户APP使用行为数据分析: 一. 背景及数据介绍: 1. 移动互联网发展背景: 网民规模7.72亿,手机网民规模7.53亿: 2. APP使用热点: 商务交易类应用规模高速增长(网络购物,网上外卖, ...

  5. Git出现 fatal: Pathspec 'xxx' is in submodule 'xxx' 异常的解决方案

    今天在使用git的时候,发现无论怎么改.gitignore文件都无法添加文件到版本控制,最后发现是缓存的原因,需要删除缓存再重新add git rm -rf --cached xxx

  6. create-react-app 构建的项目使用代理 proxy

    1. 正常运行 npm run eject (前三个步骤可省略,最好的是按照第四步操作) 2. create-react-app 的版本在低于 2.0 的时候可以在 package.json 增加 p ...

  7. Flask-login 例子

    ################################################################################ from flask.ext.logi ...

  8. ReactJS和AngularJS对比

    Angular的特点: 优势: AngularJS是一套完整的框架,angular有自带的数据绑定.render渲染.angularUI库,过滤器,$filter,$directive(模板),$se ...

  9. TNetHttpClient的用法

    TNetHttpClient的用法 TNetHttpClient是DELPHI XE8新增加的控件. 在之前,我们一般都是使用IDHTTP控件,但在安卓.IOS等非WINDOWS平台,IDHTTP访问 ...

  10. Uncaught TypeError: l.push is not a function

    layui.use([ 'jquery', 'layer', 'element' ], function() {} 而不是 layui.use( 'jquery', 'layer', 'element ...