extern “C“

目录

一、背景

二、准备工作

生成静态库

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

配置静态库

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

五、总结

一、背景

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

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

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

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

二、准备工作

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

Stack.h

cpp 复制代码
#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

cpp 复制代码
#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++新项目

cpp 复制代码
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的头文件

配置静态库

现阶段,我是一个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的函数名修饰规则去处理。

相关推荐
_.Switch3 分钟前
Python 自动化运维持续优化与性能调优
运维·开发语言·python·缓存·自动化·运维开发
徐*红4 分钟前
java 线程池
java·开发语言
尚学教辅学习资料4 分钟前
基于SSM的养老院管理系统+LW示例参考
java·开发语言·java毕设·养老院
1 9 J6 分钟前
Java 上机实践4(类与对象)
java·开发语言·算法
Code apprenticeship7 分钟前
Java面试题(2)
java·开发语言
J不A秃V头A9 分钟前
Python爬虫:获取国家货币编码、货币名称
开发语言·爬虫·python
SRY122404193 小时前
javaSE面试题
java·开发语言·面试
lb36363636363 小时前
介绍一下数组(c基础)(详细版)
c语言
无尽的大道3 小时前
Java 泛型详解:参数化类型的强大之处
java·开发语言
ZIM学编程3 小时前
Java基础Day-Sixteen
java·开发语言·windows