extern “C” 关键字的作用

在 C 语言中, extern 关键字具有外延性,使修饰的变量作用域可以拓展到其他文件中。而在C++中,extren被重载后有了其他功能,就是修饰函数以什么方式进行编译。决定了在编译过程中是否对函数进行倾轧(Name Mangling)操作,也是在今天才知道倾轧这个词语。
在C++中增加了函数重载操作,也就是可以在源代码中编写同名函数,调用者根据填写实参的不同,匹配不同的函数实现执行对应的操作,这个过程称作函数重载。而真正在编译后,函数名字并不是我们想象中真的一模一样,而是C++编译器在内部对函数名进行了倾轧操作,将相同函数名而不同形参的函数利用某些固定规则进行了改名操作,调用过程中不会因为函数名个冲突而导致出现错误,实际我们在编写源代码过程中C++这门语言只是为了给我们提供一个方便的编程环境,而并非真的使用了相同的函数名。那么具体在倾轧以后函数名变成了什么样子呢?

为了分析这个问题,我们首先要想办法看到在编译后的程序内部,函数名是什么样子的。而普通的执行文件好像是没有什么方法可以看到,至少目前我还不清楚,但是我们可以通过dll导出函数的方式,来看看C++编译的dll 和 C 编译出来的dll 到底有什么差别。

查看dll导出函数的工具:Dependency Walker

#include <stdio.h>
 
// 使用 C 编译器编译后的dll导出函数不会倾轧
 
_declspec(dllexport) int add(int a, int b)
{
	return a + b;
}
 
_declspec(dllexport) int sub(int a, int b)
{
	return a - b;
}

2015-05-07_194544

#include <iostream>
 
// 使用 C++ 编译器编译后的dll导出的所有函数都会倾轧
 
_declspec(dllexport) int add(int a, int b)
{
	return a + b;
}
 
_declspec(dllexport) int sub(int a, int b)
{
	return a - b;
}

2015-05-07_201713

如果想让C++编译器不对函数进行倾轧,可以使用 extern “C” 关键字,让其使用C语言的方式导出函数。

#include <iostream>
 
// 使用 C++ 编译器编译后的dll导出的所有函数都会倾轧
 
// 增加 extern "C" 关键字
extern "C" {
 
_declspec(dllexport) int add(int a, int b)
{
	return a + b;
}
 
_declspec(dllexport) int sub(int a, int b)
{
	return a - b;
}
 
}

2015-05-07_201839

如果想让编写出来的代码既可以在C下编译,又可以在C++下编译。可以增加一个判断,如果是C++文件就增加extern “C” 关键字,如果不是就不加,这样就可以非常灵活的使用 extren “C” 关键字了。实现如下:

#include <iostream>
 
// 使用 C++ 编译器编译后的dll导出的所有函数都会倾轧
 
// 增加 extern "C" 关键字
#ifdef  __cplusplus
extern "C" {
#endif
 
_declspec(dllexport) int add(int a, int b)
{
	return a + b;
}
 
_declspec(dllexport) int sub(int a, int b)
{
	return a - b;
}
 
#ifdef  __cplusplus
}
#endif

 

评论