【VS开发】MFC中调用C函数模块的解决方案

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


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


说明:最近调试基于MFC的程序,当通过外部C文件引入某个function的时候,又一次忘记了C文件与C++文件的区别,直接按照一般的方式,将函数声明放入头文件,将函数定义放入C文件,然后再MFC中通过包含头文件来引用对应的function,然而爆出了链接时无法定位的错误,本文就来梳理一下C++中调用C中功能函数的方法。


首先在C文件中,.c中是不允许出现任何形式的C++调用的,所以别指望C中去调用C++文件中定义的功能函数(除非使用extern “C”告诉编译器依照C的方式来编译封装接口,因为在编译生成的目标文件中,以相同的函数名为例:int foo( float x ),C接口的函数导出符号为foo与C++接口的函数导出符号_foo_float不同,因为C++编译器为了实现函数重载,会在编译时带上函数的参数信息,所以不提前声明是不能调用的,还记不记得前段时间读过的程序员的自我修养总结中的例子,我只是有点遗忘罢了。关于C中调用C++的函数,见下一个篇文章)。然后反过来,因为C++是后继的,可以包容C,所以在C++语法中有声明函数为C代码段的规定,这里说明一下几种方法:

1. 链接报错

Test.obj : error LNK2019: 无法解析的外部符号 “void __cdecl DeleteStack(struct _Node *)” (? DeleteStack@@YAXPAU_Node@@@Z),该符号在函数 _main 中被引用。

2. method 1

#ifdef __cplusplus

extern “C”

#endif

void DeleteStack(Stack stack);

如果是在C++编译器下,自动定义一个_cplusplus宏,使用extern “C”来修饰,表示外部接口函数为C的规范。当然还可以用大括号,表示集体修饰:

#ifdef __cplusplus
extern "C" {
#endif
void DeleteStack(Stack stack);
void PrintStack(Stack stack);
void Pop(Stack stack);
#ifdef __cplusplus
}
#endif

3. method 2

直接按照头文件,源文件的格式编写,直接声明整个头文件为C规范:

// CStack.h
extern "C" {
#include "Stack.h";
}

代码参考于《C++调用C函数

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

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

由于全局符号在链接过程是全局可见的,所以如果编写的库文件中和当前的目标文件中有相同的符号名,那么就会发生冲突。为了防止类似的符号名冲突,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++编译器参与的编译,则就默认定义了该宏。)。这段代码几乎在所有的系统头文件中都被利用。


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

【VS开发】MFC中调用C函数模块的解决方案的更多相关文章

  1. 如何给ioloop.run_sync()中调用的函数传入参数

    问题 如何给tornado.ioloop.IOLoop中的run_sync方法中调用的函数添加参数 解决方案 使用functools.partial 解决示例 from tornado import ...

  2. [转]用多线程方法实现在MFC/WIN32中调用OpenGL函数并创建OpenGL窗口

    原文链接: 1.用多线程方法实现在MFC/WIN32中调用OpenGL函数并创建OpenGL窗口 2.Windows MFC 两个OpenGL窗口显示与线程RC问题

  3. 【VS开发】【CUDA开发】如何在MFC中调用CUDA

    如何在MFC中调用CUDA 有时候,我们需要在比较大的项目中调用CUDA,这就涉及到MFC+CUDA的环境配置问题,以矩阵相乘为例,在MFC中调用CUDA程序.我们参考罗振东iylzd@163.com ...

  4. 在C#中调用Win32函数EnumWindows枚举所有窗口。

    原文 http://www.cnblogs.com/mfm11111/archive/2009/06/30/1514322.html 开发旺旺群发软件,难点及重要技术点分析(一) 一.        ...

  5. lua中调用C++函数

    lua中调用C++函数 我们产品中提供了很多lua-C API给用户在lua中调用,之前一直没用深究其实现原理,只是根据已有的代码在编码.显然这不是一个好的习惯,没用达到知其所以然的目的. 一.基本原 ...

  6. EC笔记,第二部分:9.不在构造、析构函数中调用虚函数

    9.不在构造.析构函数中调用虚函数 1.在构造函数和析构函数中调用虚函数会产生什么结果呢? #; } 上述程序会产生什么样的输出呢? 你一定会以为会输出: cls2 make cls2 delete ...

  7. 关于在C#中构造函数中调用虚函数的问题

    在C#中如果存在类的继承关系,应避免在构造函数中调用虚函数.这是由于C#的运行机制造成的,原因如下: 新建一个类实例时,C#会先初始化该类(对类变量赋值,并将函数记在函数表中),然后再初始化父类.构造 ...

  8. 如何在C语言中调用Swift函数

    在Apple官方的<Using Swift with Cocoa and Objectgive-C>一书中详细地介绍了如何在Objective-C中使用Swift的类以及如何在Swift中 ...

  9. C中调用Lua函数

    我们先来看一个简单的例子: lua_State* L = NULL; // 内部调用lua函数 double f(double x, double y) { double z; lua_getglob ...

随机推荐

  1. 我是如何理解Android的Handler模型_1

    Handler Message类似于旧时的电话系统,对应关系如下: 电话局->Handler 电话机->Message 接线员->handlerMessage 接线员的工作-> ...

  2. 第五章 Flask视图高级

    add_url_rule和app.route原理剖析 add_url_rule add_url_rule(rule,endpoint=None,view_func=None) 这个方法用来添加url与 ...

  3. spark job分析

    spark job spark job提交 三级调度框架, DagSch,计算stage,提交阶段,将stage映射成taskset,提交taskset给tasksch. TaskSch Backen ...

  4. sql server 函数学习

    sql server 创建函数 资料 https://docs.microsoft.com/zh-cn/sql/relational-databases/user-defined-functions/ ...

  5. java.io.ObjectInputStream类详解

    1.public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants分析 ...

  6. springboot项目没错,但就是报红叉

    1.报错原因: Description Resource Path Location TypeCannot change version of project facet Dynamic Web Mo ...

  7. LOJ2341. 「WC2018」即时战略 [动态点分治]

    LOJ 思路 考虑最蠢的暴力:枚举2~n,从1拉一条到他们的链,需要查询\(n^2\)次,显然不能通过. 考虑优化:如果拉的第一个点已经被访问过了,那么类似二分的做法,一次往那个方向多跳几步. 多跳几 ...

  8. springboot的jar在linux运行

    springboot项目使用maven打包成jar包,如何在linux优雅部署?平时启动项目使用java -jar命令,关闭程序需要查询pid再查杀进程,这样都太麻烦了,今天发现一个博客已经写好的脚本 ...

  9. Python __dict__和vars()

    1 __dict__ 设想这样一个场景.有一个字典,从某个地方获取的,比如http请求发过来的,比如从redis中hgetall出来的.我要根据这个字典来构建一个对象. 比如类 class Perso ...

  10. Oracle impdp导入数据报错:无法读取要读取的存储文件(Linux)

    当向Linux下的Oracle11g通过数据泵impdp导入数据库时,出现如图所示错误. 错误原因:bdck.dmp该为大写. 切记:Linux系统严格区分大小写.