当前位置:   article > 正文

extern“C“关键字详解_extern c

extern c

在实际项目中,如果一个C++工程需要调用C语言编写的静态库或者动态库,那么可以直接调用吗?

显然是不可以直接调用的,对于C++代码,因为C++有自己的函数名修饰规则,在编译和汇编阶段形成符号表的时候,编译器是按照C++自己的函数名修饰规则来表示符号的,而对于C的代码,也有自己的函数名修饰规则,在编译和汇编阶段,编译器是按照C的函数名修饰规则来形成符号表的,那么在链接阶段,由于符号表中表示同一函数的符号名都是不同的,链接器无法找到相应函数的地址,也就无法进行符号表的合并和重定位,就会产生链接错误。

同理,一个C工程要调用C++语言编写的静态库或者动态库,也是不可以直接调用的。

那么一个C++工程如何调用C语言编写的代码呢?一个C工程又如何调用C++语言编写的代码呢?

这实际上是一个代码可移植问题,这里就需要用到条件编译和extern "C"了。

下面演示一个在C++工程中使用C语言静态库的例子:

/*
* 注意:
* 在实现该库时,并不知道将来使用该静态库的工程是C语言工程还是C++工程
* 为了能在C/C++工程中都能使用,此时就需要用到条件编译和extern "C"
*
* __cplusplus:是C++编译器中定义的宏,C++项目会自带该宏,所以我们可以用该宏来检测是C工程还是C++工程
*
*C语言静态库定义如下
* #ifdef __cplusplus
extern "C"
{
#endif
// 要导出函数的声明
#ifdef __cplusplus
}
#endif
作用:如果是C++工程,编译器已经定义_cplusplus宏,编译时该宏是可以被识别的,被声明的函数就被extern "C"修饰了,此时C++编译器就知道,静态库中的函数是按照C的方式编译的,这样在链接时就会按照C的方式找函数名字。
如果是C工程,编译器未定义_cplusplus宏,编译时该宏无法被是被识别的,则条件编译就无效,函数就不会被extern "C"修饰,C工程调用C语言代码,当然不会有问题
*/

/*示例:
// 创建一个C语言的静态库
//calc.h///
#pragma once

#ifdef __cplusplus
extern "C"
{
#endif
int Add(int left, int right);
int Sub(int left, int right);
#ifdef __cplusplus
}
#endif
//calc.c///
#include "calc.h"
int Add(int left, int right)
{
return left + right;
}
int Sub(int left, int right)
{
return left - right;
}
//
// 创建一个c++工程,使用上面程序编程工程的静态库
#include <iostream>
using namespace std;
#include "./../Debug/calc.h"
#pragma comment(lib, "./../Debug/CalcLib.lib")

int main()
{
int ret = Add(10, 20);
cout << ret << endl;
ret = Sub(30, 20);
cout << ret << endl;
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

此时是C++工程,编译器自动定义_cplusplus宏,条件编译有效,对头文件进行文本替换后,所有函数声明都添加了extern “C”,此时C++编译器就知道静态库中的函数是按照C的方式编译的 ,那么在编译和链接阶段就会按照C的方式去找名字,就不会产生链接错误。

而如果是C工程调用该静态库,那么此时条件编译失效,对头文件进行文本替换后,所有函数声明前面没有extern “C”,C工程调用C语言编写的代码,没有任何问题。

如果没有条件编译和extern “C”,那么对于C++代码,编译器对C++代码按照C++的方式处理,在编译和汇编阶段形成符号表时就是按照C++的方式用符号表示函数,对于C代码,编译器按照C的方式处理,在编译和汇编阶段按照C的方式用符号表示函数,由于C和C++对函数名的修饰规则不同,因此,对于同一函数,C和C++表示函数的符号就不同,如果C++工程调用C的静态库,那么在链接阶段就无法通过符号名找到相应函数的地址,那么就会产生链接错误。

那如果静态库是用C++写的代码,同样,我们不知道调用该静态库的是C工程还是C++工程,因此,为保证代码的可移植性,也是一样需要对静态库中的.h文件使用条件编译和extern “C”。

// 创建一个C++语言的静态库
//calc.h///
#pragma once
#ifndef __cplusplus
extern "C"
{
#endif
int Add(int left, int right);
int Sub(int left, int right);
#ifndef __cplusplus
}
#endif
//calc.cpp///
#include "calc.h"
int Add(int left, int right)
{
	return left + right;
}
int Sub(int left, int right)
{
	return left - right;
}
//
// 创建一个c工程,使用上面程序编程工程的静态库
#include <iostream>
using namespace std;
#include "./../Debug/calc.h"
#pragma comment(lib, "./../Debug/CalcLib.lib")

int main()
{
	int ret = Add(10, 20);
	printf("%d\n",ret);
	ret = Sub(30, 20);
	printf("%d\n",ret);
	
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

如果是C工程调用该静态库,不会有_cplusplus宏,因此条件编译有效,此时所有函数声明前面就会加上extern “C”,那么对于这些函数就会按照C的方式去编译,链接阶段就不会产生问题。如果是C++工程调用该静态库,那么就是C++工程调用C++的代码,完全没有问题。

如果没有条件编译和extern “C”,那么就会产生链接错误,具体产生链接错误的原因,上面已经详细阐述过。

如果C++的静态库要被C工程调用,那么C++的静态库就不可以支持函数重载了,因为可能是按照C的方式去编译,编译的时候就无法通过。

当然,对于条件编译和宏,我们也可以这样用,以C静态库为例

// 创建一个C语言的静态库
//calc.h///
#pragma once
#ifdef _cpluscplus
	#define EXTERN_C extern "C"
#else
	#define EXTERN_C
#endif

EXTERN_C void add(int x,int y);
EXTERN_C void sub(int x,int y);
EXTERN_C void mul(int x,int y);

//calc.c///
#include "calc.h"
int Add(int left, int right)
{
	return left + right;
}
int Sub(int left, int right)
{
	return left - right;
}
//
// 创建一个c++工程,使用上面程序编程工程的静态库
#include <iostream>
using namespace std;
#include "./../Debug/calc.h"
#pragma comment(lib, "./../Debug/CalcLib.lib")

int main()
{
	int ret = Add(10, 20);
	cout << ret << endl;
	ret = Sub(30, 20);
	cout << ret << endl;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

此时,如果是C++工程调用这个C静态库,此时条件编译有效,会把函数声明前面的EXTERN_C全部替换为extern “C”,此时编译器就会按照C的方式来处理这些函数了。而如果是C工程调用这个C静态库,EXTERN_C就会消失,C工程调用C代码,不会产生任何问题。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/264368
推荐阅读
相关标签
  

闽ICP备14008679号