C++程序调用C库
假设现在有一个C文件,里面写了栈的实现以及相关操作,首先如何将一个C文件封装成库?
对项目名右键,找到属性
找到配置属性->常规->配置类型->改成静态库,如何重新编译文件
生成->重新生成解决方案,最后会生成一个.lib文件
在该项目的Debug文件下能找到一个.lib文件
至此一个C库生成完成。
然后写一段C++程序,里面要用到栈的相关操作,也就是要引用刚刚创建的C库。首先包含头文件,包含刚刚写的项目中的一个头文件(当然这是自己写的,写c文件时,h文件中是c文件相关函数的声明以及标准库中头文件的引用)。这里头文件的引用就是一个相对路径,…\表示该文件的上一个目录
当前目录
上一级目录
在再上一级目录中找到刚刚写的Stack_c文件
该文件路径的同名文件
里面有要包含的h文件
最后的引用路径。引用完头文件还需要配置,右键文件名,属性
配置属性,链接器,输入,找到附加依赖项,把要包含的库文件写到里面,文件名最后用分号分隔
然后找到常规的添加库目录,将刚刚生成的库文件路径写到里面,库文件路径可以在重新生成解决方案后下面的输出找到,但路径只要写到Debug
应用,确定。
但由于C和C++的符号表中函数修饰规则不同,C++的程序找不到一个用C写的函数,但是C++兼容C,所以在头文件包含时加上extern “C”,告诉编译器用C的规则去引用该头文件。
至此,配置完成。
代码测试
// Stack的源文件,Stack.c
#include "Stack.h"
void StackInit(Stack* st)
{
assert(st);
st->data = NULL;
st->top = 0;
st->capacity = 0;
}
void StackDestory(Stack* st)
{
assert(st);
free(st->data);
st->data = NULL;
st->capacity = 0;
st->top = 0;
}
void StackPush(Stack* st, STDataType x)
{
assert(st);
if (st->top == st->capacity)
{
//扩容,如果之前的容量是0,新的容量是4,不是0,新的容量是原来的两倍
int newcapacity = (st->capacity == 0) ? 4 : (st->capacity * 2);
st->capacity = newcapacity;
st->data = (STDataType*)realloc(st->data, sizeof(STDataType) * newcapacity);
}
st->data[st->top] = x;
st->top++;
}
void StackPop(Stack* st)
{
assert(st);
assert(st->top > 0);//top等于0,栈为空,不能删除
st->top--;
}
bool StackEmpty(Stack* st)
{
assert(st);
return (st->top == 0);
}
STDataType StackTop(Stack* st)
{
assert(st);
assert(st->top > 0);
return st->data[st->top - 1];
}
// Stack的头文件声明,Stack.h
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* data;
int top;
int capacity;
}Stack;
void StackInit(Stack* st);
void StackDestory(Stack* st);
void StackPush(Stack* st, STDataType x);
void StackPop(Stack* st);
bool StackEmpty(Stack* st);
STDataType StackTop(Stack* st);
Stack.h和Stack.c是一个项目中的,分别对应栈的函数声明与函数定义,打包库时需要一起进行编译
// 测试源文件test.cpp
extern "C"
{
#include "../../Stack_C/Stack_C/Stack.h"
}
#include <iostream>
int main()
{
Stack st;
StackInit(&st);
StackPush(&st, 1);
StackPush(&st, 2);
std::cout << StackTop(&st) << std::endl;
StackDestory(&st);
return 0;
}
(测试程序很简单,创建并初始化一个栈,将1和2入栈,打印栈顶元素,最后销毁栈,其中与栈相关的函数是以C语言的规则打包的库)如果只是包含了Stack的头文件,编译用来测试的源文件,编译器会报链接错误,即虽然有函数的声明,编译器知道这些函数,但是在链接时编译器无法找到这些函数的定义
我们已经将这些函数打包成一个库,所以现在要做的的就是告诉编译器这个库的地址与名字,具体的配置过程刚才已经说了,配置库的地址与名字,并在引用头文件时加上extern “C”,让编译器以C语言的规则调用库中的函数,此时重新运行程序
C程序调用C++库
假设栈的实现是用C++的代码写的,那现在有一个C程序,该怎么调用这个库?两个文件的配置过程类似,这里简略带过
首先是C++文件生成静态库,过程与C文件生成C库一样
C程序头文件的引用
依赖项和依赖路径的配置
需要修改的地方是:对引用的库的头文件进行条件编译。C++中有一个__cplusplus的宏,只要是C++程序就会有这个宏。由于C++是兼容C的,所以在C++程序中可以用extern “C” 的方式,告诉编译器要以C的方式去链接调用。而C程序引用这个头文件,因为条件编译,宏替换成空,extern "C"在C程序中不会出现,只会在C++程序中出现(因为C不认识extern “C”,程序会报错)。
还有一种用条件编译的方式,用extern “C”{},将函数的声明包含起来,如果定义了__cplusplus这个宏,在C++程序中,extern “C”{},就会放出来。C程序中这行代码则不会放出来。
所以合理的运用条件编译可以写出兼容性强的代码
测试代码
// Stack.h,C++库的头文件,需要使用extern "C"使程序以C的规则编译调用C++库
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* data;
int top;
int capacity;
}Stack;
/*#ifdef __cplusplus
#define EXTERN_C extern "C"
#else
#define EXTERN_C
#endif*/
//
//EXTERN_C void StackInit(Stack* st);
//EXTERN_C void StackDestory(Stack* st);
//EXTERN_C void StackPush(Stack* st, STDataType x);
//EXTERN_C void StackPop(Stack* st);
//EXTERN_C bool StackEmpty(Stack* st);
//EXTERN_C STDataType StackTop(Stack* st);
#ifdef __cplusplus
extern "C"
{
#endif
void StackInit(Stack* st);
void StackDestory(Stack* st);
void StackPush(Stack* st, STDataType x);
void StackPop(Stack* st);
bool StackEmpty(Stack* st);
STDataType StackTop(Stack* st);
#ifdef __cplusplus
}
#endif
// test.c源文件,C程序不能有extern "C"
#include "../../Stack_CPP/Stack_CPP/Stack.h"
#include <stdio.h>
int main()
{
Stack st;
StackInit(&st);
StackPush(&st, 1);
StackPush(&st, 2);
printf("%d\n", StackTop(&st));
StackDestory(&st);
return 0;
}