ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

extern “C“

2026/9/16 1:04:25 拓冰建站 浏览量
extern “C“

目录

一、背景

二、准备工作

生成静态库 

三、C++项目代用C语言的库 

配置静态库 

四、C语言的项目调用C++的库 

五、总结

一、背景

实际场景中可能出现两种情况:

1.有个东西是C++写的,现在有个C语言的程序,需要调用这个东西。

2.有个东西是C写的,现在有个C++的程序,需要调用这个东西。

这里的东西就是动态库或者静态库。

二、准备工作

这是一份博主自己封装的栈的代码

Stack.h

#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>typedef int STDataType;
typedef struct Stack
{STDataType* a;int top;int capacity;
}ST;void StackInit(ST* ps);
void StackDestroy(ST* ps);
void StackPush(ST* ps, STDataType x);
void StackPop(ST* ps);
STDataType StackTop(ST* ps);
int StackSize(ST* ps);
bool StackEmpty(ST* ps);

Stack.c


#include "Stack.h"// CĿ
void StackInit(ST* ps)
{assert(ps);ps->a = NULL;ps->top = 0; // ps->top = -1;ps->capacity = 0;
}void StackDestroy(ST* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->capacity = ps->top = 0;
}void StackPush(ST* ps, STDataType x)
{assert(ps);if (ps->top == ps->capacity){int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);if (tmp == NULL){printf("realloc fail\n");exit(-1);}ps->a = tmp;ps->capacity = newCapacity;}ps->a[ps->top] = x;ps->top++;
}void StackPop(ST* ps)
{assert(ps);assert(!StackEmpty(ps));ps->top--;
}STDataType StackTop(ST* ps)
{assert(ps);assert(!StackEmpty(ps));return ps->a[ps->top - 1];
}int StackSize(ST* ps)
{assert(ps);return ps->top;
}bool StackEmpty(ST* ps)
{assert(ps);/*if (ps->top == 0){return true;}else{return false;}*/return ps->top == 0;
}

生成静态库 

首先把他封装成静态库

 

点击创建后会弹出选项,其他选项什么也不要勾选,点击确定即可。 

 

然后把刚刚的代码分别添加到头文件与源文件里面,点击编译就生成了静态库。 

后缀是.c调用的就是C语言的编译器;后缀是.cpp就是C++的编译器 。编译一下生成.lib

三、C++项目代用C语言的库 

然后重新建立一个C++新项目

bool isValid(const char* s) {ST st;StackInit(&st);while (*s){if (*s == '('|| *s == '{'|| *s == '['){StackPush(&st, *s);  //call ? StackPush@@YAXPAUStack@@H@Z++s;}else{// 遇到右括号了,但是栈里面没有数据,说明// 前面没有左括号,不匹配,返回falseif (StackEmpty(&st)){StackDestroy(&st);return false;}STDataType top = StackTop(&st);StackPop(&st);if ((*s == '}' && top != '{')|| (*s == ']' && top != '[')|| (*s == ')' && top != '(')){StackDestroy(&st);return false;}else{++s;}}}// 如果栈不是空,说有栈中还有左括号未出// 没有匹配,返回是falsebool ret = StackEmpty(&st);StackDestroy(&st);return ret;
}int main()
{cout << isValid("{[]}") << endl;cout << isValid("([)]") << endl;//printf("%d\n", isValid("{[]}"));//printf("%d\n", isValid("([)]"));return 0;
}

在这个项目里通过相对路径的方式包上Stack.h的头文件 

配置静态库 

现阶段,我是一个C++项目,库是用C写的,编译的时候可以编译通过,但是链接的时候出错了,找这些函数的时候找不到,这是因为我们只包含了一个.h只有函数的声明。而没有函数的地址。所以我们要链接上静态库。所以静态库不是只包含了头文件就可以用的,还需要做一些配置。

然后点击应用,点击确定。这样就添加上静态库了。

再次编译,还是找不到。不是已经添加库了吗?

原因就在于库是.c的。

我们把库改成.cpp然后编译。

这个时候执行程序,运行成功。

所以C++项目去调用C++的库,经过配置完后是完全没有问题的。

但是只要我们把库改成.c就,源.cpp这个程序就运行出错了。这个库是完全可以用C语言写的。我用C++的项目去调用这个库,显然是调用失败的,原因就是C语言和C++生成的函数名修饰规则不一样。C语言直接用的就是函数名,C++则有自己的规则,g++的函数名修饰规则_Z+函数名长度+函数名+参数首字母。

eg:windows下调用StackPush按照这样的名字(StackPush@@YAXPAUStack@@H@Z)去找该函数地址(去编译出来的D_S.lib里面去找这个函数地址)。但是D_S.lib它的符号表里面没有这个名字+地址的映射。因为D_S.lib是C语言写的,按照C语言的规则,C语言是没有函数名修饰规则的,直接就是这个StackPush这个名字+地址的映射。所以根本就找不到。 

这个时候为了让C++调用C。就可以使用extern C。

用上这个语法后,就告诉了C++的编译器,这些代码(指C语言写的静态库)是用C的风格去编译的,接下来你去链接的时候不要用StackPush@@YAXPAUStack@@H@Z这样的名字去找函数,而是用StackPush这样的名字去找,才能找到。 

此时程序正确运行。

C++项目要用C实现的DS静态库:1.包含文件夹 2.在工程属性中配置静态库的目录和添加静态库。

四、C语言的项目调用C++的库 

程序是C语言,库是C++

C程序报错。找不到函数,原因一样是函数名修饰规则。因为库是C++写的,它的函数名是被修饰的。所以C程序同样找不到。 

所以我们就要在静态库里面进行修改。

此时发现C程序可以正常运行了。但是这个仅仅是个随机事件,这种情况是特定于使用Visual Studio 2019的C++编译器的优化行为,它检测出使用了extern C,用了C++的编译器去编译了.c代码。在别的环境下是会报错的,预处理阶段会把包含的.h文件展开,又因为extern C是C++特有的语法,C的编译器是压根不认识它的,所以就会报错。 

所以这样写是不行的,这个程序成功只是特殊情况,不具有普遍性。

解决方案 

__cplusplus是C++特有的标识符,如果检测到__cplusplus说明是C++的编译器,如果没有检测到__cplusplus说明是C的。这样的意思就是如果检测到了__cplusplus就用extern C用C的函数名修饰规则。这样的话就既能让C++的动态库按C的函数名修饰规则去编译,又能让C程序栈预处理阶段不报错。

或者这样进行使用 

五、总结

C++程序调用C的库,在C++程序中加extern "C"。在C++项目里,告诉C++编译器,extern C{}里面的函数是C编译器编译的,链接的时候用C的函数名规则去找,就可以链接上。

C程序调用C++的库。在C++库中加extern "C"。在C++静态库,extern C告诉编译器以下函数按照C的函数名修饰规则去处理。