宏学习记录001 —— #ifndef、__declspec(dllexport)、extern “C“

一、简介

语言:C++

参考链接:

        #ifndef的用法

        __declspec(dllexport)的用法

参考链接里面已经讲的很明白了,这里我再结合我遇到的具体的实例讲解一下,给自己加深一下映像。

二、举例说明

在我写的一篇文章“C++学习记录003——创建DLL(“__declspec(dllexport)“ 版本,无需自己写DEF 文件)并调用”中,DLL 定义导出接口函数是这么写的,如下所示:

//MathAPI.h
#pragma once
 
#ifndef _MATHAPI_H_
#define _MATHAPI_H_
 
#define MATH_Export
 
#ifdef MATH_Export
#define MathApi __declspec(dllexport)
#else
#define MathApi __declspec(dllimport)
#endif 
 
#ifdef __cplusplus
extern "C"
{
	MathApi int Add(int a,int b);
	MathApi int Minus(int a,int b);
}
#else
    MathApi int Add(int a, int b);
    MathApi int Minus(int a, int b);
#endif
 
#endif

(一)首先最外层的框架

#ifndef _MATHAPI_H_
#define _MATHAPI_H_

......


#endif

即前面所给的实例中见下图红色框框住的部分。

 关于头文件中这个框架的作用,我前面的第一个参考链接中已经讲的很清楚了,这里就直接引用了。

头件的中的#ifndef,这是一个很关键的东西。

需要注意的是,#ifndef起到的效果是防止一个源文件两次包含同一个头文件,
而不是防止两个源文件包含同一个头文件。

而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。

还是把头文件的内容都放在#ifndef和#endif中吧。
不管你的头文件会不会被多个文件引用,你都要加上这个。

总结就一句话:防止过多不必要的引用

(二)其次“__declspec(dllexport)"的用法

#define MATH_Export
 
#ifdef MATH_Export
#define MathApi __declspec(dllexport)
#else
#define MathApi __declspec(dllimport)
#endif 

上面这几行代码,翻译成人话就是:

定义 MATH_Export

如果定义了MATH_Export, 则用定义MathApi 替代 __declspec(dllexport),表示输出接口
如果没有定义MATH_Export,则用定义MathApi 替代 __declspec(dllimport),表示输入接口


有了这几行语句之后,就可以在函数定义时在前面加上 MathApi 来导出(或导入接口)

本实例中,因为前面定义了 MATH_Export,所以是导出接口

即实例中的:

 同样,__declspec(dllexport) 的作用在第二个链接中有说明。

总结一句话: __declspec(dllexport) 用于导出某个函数接口

(三)extern "C" 的用法

#ifdef __cplusplus   //如果是C++代码
extern "C"    //以处理 C 语言代码的方式来处理C++代码
{
	MathApi int Add(int a,int b);
	MathApi int Minus(int a,int b);
}
#else
    MathApi int Add(int a, int b);
    MathApi int Minus(int a, int b);
#endif
在C++出现以前,很多代码都是C语言写的,而且很底层的库也是C语言写的,为了更好的支
持原来的C代码和已经写好的C语言库,需要在C++中尽可能的支持C,而extern "C"就是其
中的一个策略。

所以在使用 C 和 C++ 进行混合编程时,考虑到对函数名的处理方式不同,为了避免造成编译器在
程序链接阶段无法找到函数具体的实现,导致链接失败,会在前面加上extern "C" 来修饰。

对函数名的处理方式:
(1)C++ 
    C++会在程序的编译阶段对函数的函数名进行“再次重命名”,例如:
    void Swap(int a, int b) 会被重命名为_Swap_int_int;
    void Swap(float x, float y) 会被重命名为_Swap_float_float。
    显然通过重命名,可以有效避免编译器在程序链接阶段无法找到对应的函数。
(2)C
    C不会在编译阶段对函数的名称做较大的改动,例如
    void Swap(int a, int b) 会被重命名为_Swap;
    void Swap(float x, float y) 会被重命名为_Swap。

"extern"是 C 和 C++ 的一个关键字,但对于 extern "C",可以将其看做一个整体,和 extern 毫无关系。
extern "C" 既可以修饰一句 C++ 代码,也可以修饰一段 C++ 代码,它的功能是让编译器以处理 C 语言代码的方式来处理修饰的 C++ 代码。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值