【C/C++基础】extern “C“ __declspec(dllexport)详解

部分参考自:https://blog.csdn.net/fengbingchun/article/details/78825004
仅作学习记录

前置知识

1. 条件编译

具体请阅读 blog

条件编译(conditional compiling)命令指定预处理器依据特定的条件来判断保留或删除某段源代码。
例如,可以使用条件编译让源代码适用于不同的目标系统,而不需要管理该源代码的各种不同版本。
在这里插入图片描述

2. 动态库.dll/.so 与 静态库.lib/.a的区别

具体请阅读 blog

在编写算法组件时,如果给业务集成人员提供 公共头文件 和 动态算法库:

  1. 代码组件在linux下编译,提供 .so,则在public头文件中的对外 函数声明上,无需加 关键字去修饰,因为linux下的动态库接口全是对外开放的
  2. 代码组件在windows下编译,提供 .dll, 则要在public头文件中的 函数声明上,加上EXPORT关键字表明该接口是对外可被调用的
    //Dll导出支持
    #define EXPORT __declspec(dllexport)
    

3. extern "C"解释

具体请阅读 blog

使用 C 和 C++ 进行混合编程时,考虑到对函数名的处理方式不同,势必会造成编译器在程序链接阶段无法找到函数具体的实现,导致链接失败。
extern “C” 既可以修饰一句 C++ 代码,也可以修饰一段 C++ 代码,它的功能是让编译器以处理 C 语言代码的方式来处理修饰的 C++ 代码。

void Swap(int a, int b) 会被重命名为_Swap_int_int;

C编译后,函数名变为 _Swap
C++编译后,因为有重载语法的原因,函数名变为 _Swap_int_int

大白话阐述:
就是C编写的函数,按c++的编译规则 编译出的名字 和 C编出来的不同,导致在c++代码里调用C函数是找不到。
加了extern “C”{ … }后,让 其包含的内容 也按照C语言的方式编译。

#ifdef __cplusplus //如果是c++代码 {}内按照C来编译,不是就正常编译
extern "C" {
#endif

void display();
...其他声明...

#ifdef __cplusplus
}
#endif

4. C/C++中一些预定义宏

具体请阅读 blog
在这里插入图片描述

__declspec(dllexport)具体阐述

1. 概念解释

__declspec是Microsoft VC中专用的关键字,它配合着一些属性可以对标准C/C++进行扩充。__declspec关键字应该出现在声明的前面

__declspec(dllexport)用于Windows中的动态库中声明导出函数、类、对象等供外面调用,省略给出.def文件。即将函数、类等声明为导出函数,供其它程序调用,作为动态库的对外接口函数、类等

.def文件(模块定义文件)是包含一个或多个描述各种DLL属性的Module语句的文本文件.def文件或__declspec(dllexport)都是将公共符号导入到应用程序或从DLL导出函数。如果不提供__declspec(dllexport)导出DLL函数,则DLL需要提供.def文件。

__declspec(dllimport)用于Windows中,从别的动态库中声明导入函数、类、对象等供本动态库或exe文件使用。当你需要使用DLL中的函数时,往往不需要显示地导入函数,编译器可自动完成。不使用__declspec(dllimport)也能正确编译代码,但使用__declspec(dllimport)使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于DLL中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨DLL边界的函数调用中。声明一个导入函数,是说这个函数是从别的DLL导入。一般用于使用某个DLL的exe中

2. 代码示例

此语法只用于 windows 下的动态库编写。

以下是测试代码:新建一个动态库工程Library,然后在CppBaseTest工程中调用Library的接口:

  • library.hpp
#ifndef FBC_LIBRARY_LIBRARY_HPP_
#define FBC_LIBRARY_LIBRARY_HPP_
 
// reference: http://geoffair.net/ms/declspec.htm
 
#ifdef _MSC_VER
	#ifdef FBC_STATIC
		#define FBC_API
	#elif defined FBC_EXPORT
		#define FBC_API __declspec(dllexport)
	#else
		#define FBC_API __declspec(dllimport)
	#endif
#endif
 
#ifdef __cplusplus
extern "C" {
#endif
 
FBC_API int library_add(int a, int b);
FBC_API int value;
 
#ifdef __cplusplus
}
#endif
 
template<typename T>
class FBC_API Simple {
public:
	Simple() = default;
	void Init(T a, T b);
	T Add() const;
 
private:
	T a, b;
};
 
 
#endif // FBC_LIBRARY_LIBRARY_HPP_
  • library.cpp
#include "library.hpp"
#include <iostream>
#include <string>
 
FBC_API int library_add(int a, int b)
{
	value = 11;
 
	fprintf(stdout, "File: %s, Function: %s, Line: %d\n", __FILE__, __FUNCTION__, __LINE__);
	return (a+b);
}
 
template<typename T>
void Simple<T>::Init(T a, T b)
{
	this->a = a;
	this->b = b;
}
 
template<typename T>
T Simple<T>::Add() const
{
	fprintf(stdout, "File: %s, Function: %s, Line: %d\n", __FILE__, __FUNCTION__, __LINE__);
	return (a + b);
}
 
template class Simple<int>;
template class Simple<std::string>;
  • test_library.hpp
#ifndef FBC_CPPBASE_TEST_TEST_LIBRARY_HPP_
#define FBC_CPPBASE_TEST_TEST_LIBRARY_HPP_
 
#include <library.hpp>
 
namespace test_library_ {
 
#ifdef __cplusplus
	extern "C" {
#endif
 
__declspec(dllimport) int library_add(int, int);
__declspec(dllimport) int value;
 
#ifdef __cplusplus
	}
#endif
 
int test_library_1();
int test_library_2();
 
} // namespace test_library_
 
#endif // FBC_CPPBASE_TEST_TEST_LIBRARY_HPP_
  • test_library.cpp
#include "test_library.hpp"
#include <iostream>
#include <string>
 
#include <library.hpp>
 
namespace test_library_ {
 
int test_library_1()
{
	int a{ 4 }, b{ 5 }, c{ 0 };
 
	c = library_add(a, b);
	fprintf(stdout, "%d + %d = %d\n", a, b, c);
	fprintf(stdout, "value: %d\n", value);
 
	return 0;
}
 
int test_library_2()
{
	Simple<int> simple1;
	int a{ 4 }, b{ 5 }, c{ 0 };
 
	simple1.Init(a, b);
	c = simple1.Add();
	fprintf(stdout, "%d + %d = %d\n", a, b, c);
 
	Simple<std::string> simple2;
	std::string str1{ "csdn blog: " }, str2{ "http://blog.csdn.net/fengbingchun" }, str3;
 
	simple2.Init(str1, str2);
	str3 = simple2.Add();
	fprintf(stdout, "contents: %s\n", str3.c_str());
 
	return 0;
}
 
} // namespace test_library_

注意理解library.hpp中FBC_API的条件编译可以表示静态链接库接口、动态链接库导出接口、动态链接库导入接口,通过VS中预编译宏(配置属性->C/C+±>预处理器->预处理器定义)的定义来实现不同的功能;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值