C使用C++编译出来的动态链接库,C++使用C编译出来的动态链接库,C使用C编译出来的动态链接库,C++使用C++编译出来的动态链接库……
最近和师兄们搭伴做加密系统,自己负责加密算法部分,师兄们负责更加底层的硬件接口部分。但是因为个人原因我不大喜欢 C
语言,当然是相比较于 C++
而言,所以自己就用了 C++
去实现加密算法。这么一来,就涉及到和师兄写的程序之间接口问题,于是乎就研究了下怎样导出动态链接库以供他人使用。
对于 C
和 C++
之间的关系,可不是 Java
和 JavaScript
之间的关系,所以操作起来相对而言还是比较容易的,但是也有一些坑需要注意,要不然也不至于在2020年的国庆节折腾这折腾了两天 ,难度系数表如下表:
![0b73a67d89029e9ba445ca383b03dc69.png](https://i-blog.csdnimg.cn/blog_migrate/be804029724e9c4155d6ebc0195d1f80.png)
因为 C/C++
对系统环境比较敏感,这里列出写这篇文章时的系统情况:
![f19b11dede5a57abbd5ebf930c089711.png](https://i-blog.csdnimg.cn/blog_migrate/101c979e88bf422fa2a3534d2e2cc630.png)
噢,对了,还有重要的 C
和 C++
版本:
C
:C99C++
:C++17
1. C导出动态链接库给C++用
什么?上来为什么要说这个?那肯定是因为简单啊!那为什么不说另外两个更简单的?那肯定是因为没做啊
先来考虑这样一个需求:小张小明是朋友,小张喜欢 C++
,小明不得不用 C
,而小明需要给小张提供一些库,小张使用小明提供的库进行上层开发,那么也就是:
- 小明使用
C
语言写了个驱动,导出DLL
动态链接库 - 小张拿到小明导出的
DLL
动态链接库,引入到项目中,使用在DLL
中编译好的函数(或数据结构)
那么他俩可以这样做:
1.1 小明的工作目录
小明的工作目录长这样:
D:.
│ CMakeLists.txt
│ library.c
│ library.h
└─cmake-build-debug
其中的 cmake-build-debug
是用来存放 CMake
执行过程中产生的文件,可以暂时忽略不计,主要关注这几个文件的内容:
CMakeLists.txt
规定了怎样去编译这个库,怎样导出。这里主要说明了项目名称,使用的语言,C
语言的标准用哪一版,以及库的依赖文件和导出方式(SHARED
)
cmake_minimum_required(VERSION 3.17)
project(C_Lib C)
set(CMAKE_C_STANDARD 99)
add_library(C_Lib SHARED library.c library.h)
library.h
函数的声明,简简单单,普普通通
#ifndef C_LIB_LIBRARY_H
#define C_LIB_LIBRARY_H
void hello(void);
#endif //C_LIB_LIBRARY_H
library.c
函数的实现,更加朴素
#include "library.h"
#include <stdio.h>
void hello(void) {
printf("Hello, World!n");
}
完成这些之后,点击编译项目或 Ctrl+F9
快捷键编译或者输入命令编译:
cmake.exe --build .cmake-build-debug --target C_Lib -- -j 6
完成后可以看到如下输入:
[100%] Built target C_Lib
那么可以在 cmake-build-debug
目录下找到 libC_Lib.dll
文件,这个文件就是用来给小张用的。
好了,小明可以去吃苹果了 。
1.2 小张的工作目录
小张的工作目录长这样:
D:SOURCEC++TEMPCXX_EXE
│ CMakeLists.txt
│ libC_Lib.dll
│ main.cpp
└─cmake-build-debug
这里主要多了一个 libC_Lib.dll
文件,这个文件就是来自小明的,那么该如何使用里面定义的 hello()
方法?
- 首先,需要导入到当前项目中,这里使用
CMakeLists.txt
来完成:
cmake_minimum_required(VERSION 3.17)
project(CXX_Exe)
set(CMAKE_CXX_STANDARD 17)
link_directories(.) # 从当前目录中寻找链接文件
add_executable(CXX_Exe main.cpp library.h)
target_link_libraries(CXX_Exe libC_Lib.dll) # 将 libC_Lib.dll 链接到 CXX_Exe 目标文件中
- 使用头文件
library.h
中引入函数声明
#ifndef C_LIB_LIBRARY_H
#define C_LIB_LIBRARY_H
#ifdef __cplusplus
extern "C" {
#endif
void hello();
#ifdef __cplusplus
}
#endif
#endif //C_LIB_LIBRARY_H
- 在
C++
源文件main.cpp
中使用hello()
函数
#include "library.h"
int main() {
hello(); // >: Hello, World!
return 0;
}
不过!重点之一来了!为什么在头文件 library.h
中写一个 extern "C"
呢?是因为 C
的编译链接过程,中间代码不会修改函数名称,而 C++
在这个过程中将会修改!因此,为了让 C++
识别出来 C
函数,需要使用 extern "C"
来指明,这个函数是 C
过来的。
否则会异常退出:undefined reference to 'hello()' collect2.exe: error: ld returned 1 exit status
。
**重点之二,**在 CLion
中,仅仅在 CMakeLists.txt
中声明怎样链接,怎样找动态链接库是不够的,还需要在 Run Configurations
设置中去添加环境变量 PATH={DLL文件所在的目录}
才可以,要不然就直接放到 CLion
已知的环境目录下。
否则会异常退出: Process finished with exit code -1073741515 (0xC0000135)
。
至此,小张小明合作愉快,任务完成 。
2. C++导出动态链接库给C用
还是小张和小明,还是 C
和 C++
,只不过这次换成了小张提供动态链接库给小明使用。
2.1 小张的工作目录
小张需要提供动态链接库,他的工作目录长这样:
D:.
│ CMakeLists.txt
│ library.cpp
│ library.h
└─cmake-build-debug
还是跟之前一样, cmake-build-debug
里面找动态链接文件,而 CMakeLists.txt
里面规定了怎样去编译这个程序。
挨着牌的来康康每个文件写了啥:
CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
project(CXX_Lib)
set(CMAKE_CXX_STANDARD 17) # 设置 C++ 标准为 17
add_library(CXX_Lib SHARED library.cpp library.h) # 规定了依赖哪些文件去产生动态链接文件,以及动态链接文件类型
library.h
#ifndef CXX_LIB_LIBRARY_H
#define CXX_LIB_LIBRARY_H
extern "C"
// 声明下面这个函数,是要按照 C 语言的标准去导出的,不要改名啦~
void hello();
#endif //CXX_LIB_LIBRARY_H
这里的不同在于,直接在动态链接库的头文件中声明了 extern "C"
而不是在用到动态链接文件的地方。
library.cpp
#include "library.h"
#include <iostream>
void hello() {
std::cout << "Hello, World!" << std::endl;
}
经历和小明一样的编译过程:
cmake.exe --build .cmake-build-debug --target CXX_Lib -- -j 6
然后在 cmake-build-debug
目录下找到 libCXX_Lib.dll
文件,然后丢给小明,然后小张去喝牛奶 了。
2.2 小明的工作目录
小明拿到小张的动态链接文件后,直接放到工作目录下,然后他的工作目录就长这样了:
D:.
| CMakeLists.txt
| libCXX_Lib.dll
| library.h
| main.c
---cmake-build-debug
其中的 libCXX_Lib.dll
就是小张那取过来的。
- 一样的流程,首先使用
CMakeLists.txt
导入到项目中
cmake_minimum_required(VERSION 3.17)
project(C_Exe C)
set(CMAKE_C_STANDARD 99)
link_directories(.) # 从哪里找链接文件
add_executable(C_Exe main.c library.h)
target_link_libraries(C_Exe libCXX_Lib.dll) # 使用这个动态链接库
- 然后声明头文件
library.h
#ifndef CXX_LIB_LIBRARY_H
#define CXX_LIB_LIBRARY_H
void hello();
#endif //CXX_LIB_LIBRARY_H
- 最后使用
main.c
#include <stdio.h>
#include "library.h"
int main() {
hello(); // >: Hello, World!
return 0;
}
虽然小明这里没有了 extern "C"
的注意事项,但是仍然需要在 CLion
的 Run Configurations
里面配置 PATH={DLL文件所在的目录}
才能使用,否则还是报 Process finished with exit code -1073741515 (0xC0000135)
。
好了,小明终于可以去休息了,我也去休息一下 ~~~
写完后突然想起来,我和师兄为啥不统一一下语言呢? 省时省力还省得这两天折腾来折腾去。
不过想想,有了这个经验,以后可以专门给 Java
、Python
等程序写动态链接库,也未尝不是件好事。
真是让人恍然大明白!
公众号:Algorithms