extern “C“

目录

一、背景

二、准备工作

生成静态库 

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

配置静态库 

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

五、总结

一、背景

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

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

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

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

extern “C“_第1张图片

二、准备工作

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

Stack.h

#pragma once

#include 
#include 
#include 
#include 

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;
}

生成静态库 

首先把他封装成静态库

extern “C“_第2张图片 extern “C“_第3张图片

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

extern “C“_第4张图片 

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

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

extern “C“_第5张图片

三、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
		{
			// 遇到右括号了,但是栈里面没有数据,说明
			// 前面没有左括号,不匹配,返回false
			if (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;
			}
		}
	}

	// 如果栈不是空,说有栈中还有左括号未出
	// 没有匹配,返回是false
	bool 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的头文件 

extern “C“_第6张图片

配置静态库 

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

extern “C“_第7张图片

extern “C“_第8张图片

extern “C“_第9张图片extern “C“_第10张图片然后点击应用,点击确定。这样就添加上静态库了。

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

extern “C“_第11张图片

原因就在于库是.c的。

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

extern “C“_第12张图片这个时候执行程序,运行成功。

extern “C“_第13张图片

所以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这个名字+地址的映射。所以根本就找不到。 

extern “C“_第14张图片

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

extern “C“_第15张图片

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

此时程序正确运行。

extern “C“_第16张图片

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

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

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

extern “C“_第17张图片

extern “C“_第18张图片

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

extern “C“_第19张图片

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

extern “C“_第20张图片extern “C“_第21张图片

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

extern “C“_第22张图片

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

解决方案 

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

extern “C“_第23张图片

或者这样进行使用 

extern “C“_第24张图片

五、总结

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

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

你可能感兴趣的:(C++,c语言,开发语言,extern,C)