VS开发】C中调用C++文件中定义的function函数
2015-12-04 18:51
246 查看
【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 调试记录 张朋艺
相关文章推荐
- 学习笔记——关于指向常量指针的解引用的发现
- C++直接初始化和复制初始化2
- C++直接初始化和复制初始化1
- C语言之字符串
- 【C语言提高24】二级指针做输入的第一种内存模型:数组指针
- 解决iOS报错libc++abi.dylib handler threw exception
- 【C语言提高23】二级指针做输出的内存模型
- c语言中static用法总结
- VS2008 C++ 调用托管C++dll 当前不会命中断点,没有与此关联的代码
- C++ (Bug)--调 C文件,报错 xxxx.0 not fountt, x86
- C++ Timer定时器
- Vector c++ 的基本操作 矢量 *_* !
- printf 格式化输出符号详细说明
- C++终极复习篇(五)----STL标准模板库
- MFC 学习之 鼠标移动到Toolbar按钮上显示提示信息(tooltip),状态栏也随之改变
- c语言学习
- C++中头文件的概念与基本编写方法
- C++ string 大小写互转(八~二)
- C语言指针笔记 by STP
- C语言函数入栈顺序