1.extern关键字
extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其他模块中使用。与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块使用
实例:
1)变量的调用
在extern1.cpp中定义一个全局变量:
int a = 12;
在externMain.cpp中:
#include <iostream>
using namespace std;
int a;
int main()
{
cout << a << endl;
system("pause");
return 0;
}
这样编译就会出现错误:
这时需要在externMain.cpp中,在int a前面添加关键字extern
extern int a;
这时便能成功打印出来变量a的值了,注意变量前面的类型不能省略,否则编译器报错:缺少类型说明符
如果我们在extern1.cpp中,在int a前面添加static修饰符,再编译会报错:
说明static修饰的变量不能在其他文件中使用
2)函数的调用
在extern1.cpp中,定义一个函数:
#include <iostream>
using namespace std;
void fun()
{
cout << "helloworld" << endl;
}
在externMain.cpp中调用该函数:
#include <iostream>
using namespace std;
int main()
{
fun();
system("pause");
return 0;
}
如果这样直接编译的话,会报错:
需要在函数使用前进行声明:
#include <iostream>
using namespace std;
void fun();
int main()
{
fun();
system("pause");
return 0;
}
如果我们在函数声明的地方加上extern关键字,程序也是可以运行的:
extern void fun();
对于外部函数,extern可以省略,但对于外部变量,extern就不能被省略
2. extern "C"关键字
extern "C"指示编译器这部分代码按C语言方式编译和链接进行编译,而不是C++的方式
3.C函数与C++函数编译的区别
C++是面向对象的语言,支持函数重载,而C不支持。函数被C++编译后在符号库中的名字和C语言中的不同。例如,某函数原型为:
void foo(int x, int y);
该函数被C编译器编译后在符号库中名字为foo或_fun@4,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能产生的名字不同,这样的名字包含了函数名、函数参数数量及类型信息,而不再是函数原来的名字了),C++就是靠这种机制实现函数重载的
实例:
1)之前DLL项目产生的.dll文件,在Dependency中打开查看:
因为之前在函数导出时,我们在前面添加了extern “C”,所以现在我们发现,函数的名字跟我们定义的函数名字一样
现在我们在DLL项目去去掉extern “C”:
#include "func.h"
#include "stdafx.h"
_declspec(dllexport) int SquareSum(int a, int b)
{
return (a*a + b * b);
}
_declspec(dllexport) int SumSquare(int a, int b)
{
return ((a + b)*(a+b));
}
重新生成后在Dependency中打开查看:
我们发现,函数名称已经改变,与我们定义的函数名字不一样了
此时如果我们继续在.exe文件中调用该.dll文件便会找不到该函数地址,程序不会执行
如果我们使用Dependency中的函数名,发现程序又会正常运行了
#include <iostream>
#include <windows.h>
using namespace std;
//#include "func.h"
//不需要.h头文件和.lib文件
//#pragma comment(lib,"DynamicLib64.lib")
int main()
{
int a = 34;
int b = 32;
//加载DLL文件
HMODULE h = LoadLibrary("DynamicLib32.dll");
//定义一个函数指针类型
typedef int(*PFUNC)(int, int);
//通过函数名称来获取函数地址
PFUNC pSquareSum = (PFUNC)GetProcAddress(h, "?SquareSum@@YAHHH@Z");
PFUNC pSumSquare = (PFUNC)GetProcAddress(h, "?SumSquare@@YAHHH@Z");
//通过函数序号来获取函数地址
//PFUNC pSquareSum = (PFUNC)GetProcAddress(h, (char*)1);
//PFUNC pSumSquare = (PFUNC)GetProcAddress(h, (char*)2);
//调用函数
cout << pSquareSum(a, b) << endl;
cout << pSumSquare(a, b) << endl;
//释放DLL文件
FreeLibrary(h);
system("pause");
return 0;
}
执行结果:
从该例子中我们也可以简单的总结出:extern “C”的作用就是让函数在编译时按照C语言的方式编译,保证函数名称不改变
4. extern “C”的使用场景
1)在C++环境下使用C语言方式编译DLL,保证函数名称不改变:
extern "C" _declspec(dllexport) int SquareSum(int a, int b)
{
return (a*a + b * b);
}
extern "C" _declspec(dllexport) int SumSquare(int a, int b)
{
return ((a + b)*(a+b));
}
2)在C++中引用C语言中的函数和变量时,需要在包含C语言函数和变量的头文件的位置进行下列处理:
extern "C"
{
#include "extern1.h"
}
实例:
在extern1.c中:
#include "stdio.h"
int a = 12;
void fun()
{
printf("helloworld\n");
}
在externMain.cpp中:
#include <iostream>
using namespace std;
extern int a;
void fun();
int main()
{
cout << a << endl;
fun();
system("pause");
return 0;
}
编译错误:
修改方法:在每个变量和函数声明前面添加extern “C”
#include <iostream>
using namespace std;
extern "C" int a;
extern "C" void fun();
int main()
{
cout << a << endl;
fun();
system("pause");
return 0;
}
执行结果:
如果extern1.c中变量和函数太多,我们每次都这样添加extern “C”很不方便,所以我们可以在包含extern1.cpp中函数和变量的头文件上加extern “C”
extern1.h文件中:
#pragma once
extern int a;
void fun();
externMain.cpp中:
#include <iostream>
using namespace std;
extern "C"
{
#include "extern1.h"
}
int main()
{
cout << a << endl;
fun();
system("pause");
return 0;
}
这样也会执行成功:
注意:
extern “C”不允许在C语言中使用,否则编译错误
为了在C语言和C++语言中都能正常使用,我们可以使用标准写法,利用宏定义:
#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus
#include "extern1.h"
#ifdef __cplusplus
}
#endif // __cplusplus
这样就能在C语言中不调用extern “C”{ },而在C++中调用extern “C”{ },是代码具有了通用性
5.注意事项
1)在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern “C”声明,在.c文件中包含extern “C”时会出现编译语法错误
2)把extern “C”放在class的前面,编译器还是会忽略掉,最后产生的还是C++修饰符,而不是C修饰符