zoukankan      html  css  js  c++  java
  • 【VS开发】MFC中调用C函数模块的解决方案

    【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 调试记录 张朋艺

  • 相关阅读:
    【贪心算法】POJ-3040 局部最优到全局最优
    【贪心算法】POJ-1017
    【贪心算法】POJ-2393 简单贪心水题
    【贪心算法】POJ-3190 区间问题
    项目选题报告(团队)
    结对项目第一次作业——原型设计
    第三次作业团队展示
    软工实践第二次作业——数独
    软件工程实践第一次作业--准备
    字符串
  • 原文地址:https://www.cnblogs.com/huty/p/8518925.html
Copyright © 2011-2022 走看看