extern "C"
在C++引用lua的头文件时,我们总会写成:
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
为什么要这么做呢?原因是C++的编译器会对程序中符号进行修饰,这个过程在编译器中叫符号修饰(Name Decoration)或者符号改编(Name Mangling)。同时我们知道C++是能够兼容C的,如果我们有了一个C语言的头文件和其对应的库,在C++中如何使用它呢?在include该头文件的时候当然要加入extern "C",否则按照C++的符号进行符号修饰,那么在库中就会找不到该符号了。
告诉编译器这段代码是以C编译器来编译,底层函数签名就是函数名称,而不是C++那样的函数名+参数,不支持重载的。
extern
C++语言支持分离式编译机制,该机制允许将程序分割为若干个文件,每个文件可被独立编译。为了将程序分为许多文件,则需要在文件中共享代码,例如一个文件的代码可能需要另一个文件中中定义的变量。
为了支持分离式编译,C++允许将声明和定义分离开来。变量的声明规定了变量的类型和名字,即使一个名字为程序所知,一个文件如果想使用别处定义的名字则必须包含对那个名字的声明。定义则负责创建与名字关联的实体,定义还申请存储空间。
如果想声明一个变量而非定义它,就在变量名前添加extern关键字,而且不要显式地初始化变量:
extern int i; //声明i而非定义
int j; //声明并定义j
但我们也可以给由extern关键字标记的变量赋一个初始值,但这样就不是一个声明了,而是一个定义:
extern int v = 2;
int v = 2; //这两个语句效果完全一样,都是v的定义,所以如果编译就会报错
注意: 变量能且只能被定义一次,但是可以被声明多次。
根据C++标准的规定,一个变量声明必须同时满足两个条件才是声明,否则就是定义:
1、声明必须使用extern关键字;
2、不能给变量赋初值
extern int a; //声明
int a; //定义
int a = 0; //定义
extern int a = 0; //定义
extern比较普遍的用法是,在一个.c文件中extern声明一个在别的.cpp文件中已经定义的变量(一定要是全局变量),然后在本.cpp文件中操作的就是在那个文件中的变量,相当于跨文件引入一个变量,相当于对.c文件的include。
头文件定义任何变量都是非常业余的行为,因为如果在头文件中定义,如果这个头文件被多个cpp引用(include),会造成重复定义的链接错误。
所以:通常的做法是,在.h文件中用extern声明(不定义)一个变量,然后在想用到这个变量的.cpp文件中用staitc定义这个变量(不定义是没有办法使用的),则该变量只在当前cpp文件中有效,在别的文件中无效。如果有两个cpp都定义了这个变量而都没有加static,也会报错。
extern 和 static
1、extern 表明该变量在别的地方已经声明过了,在这里要使用那个变量;
2、static 表示静态的变量,分配内存的时候存储在静态区,不存储在栈上面。
【注意】
1、extern和static不能同时修饰一个变量
2、static修饰的全局变量声明与定义同时进行
3、static修饰全局变量的作用域只能是本身的编译单元
extern 和 const
C++中const修饰的全局常量具有跟static相同的特性,即它们只能作用于本编译模块中,但是const可以与extern连用来声明该常量可以作用于其他编译模块中,如在头文件中写extern const char g_str[]。
然后在cpp中定义:const char g_str[] = "123456"(不同的cpp中可以定义不同的字符串)。
所以当const单独使用时它就与static相同,而当与extern一起时就与extern相同!
编译器编译过程
//main.cpp
#include "stdafx.h"
#include "ExternExample.h"
int _tmain(int argc, _TCHAR* argv[])
{
using namespace std;
cout << temp << endl;
system("pause");
return 0;
}
//ExternExample.h
int temp;
1、预编译
所谓预编译,其实就是#include展开和宏展开,上述代码被展开后,main.cpp里面就了int temp这个定义,注意是定义。
2、编译
编译是将刚刚展开之后的.h中的内容和.cpp中的内容作为一个编译单元来进行编译,每个.cpp文件都会按这种方式被编译成一个.obj文件。
3、连接
编译好之后的obj是独立的,需要连接在一起,才能成为一个程序。
【总结】
1、.h头文件中一定不要有定义,只能有声明,定义全部写在.cpp文件里面;
2、在头文件中,需要使用条件编译(windows中可以用#pragma once代替),防止重定义。
#ifndef _EXTERN_EXAMPLE
#define _EXTERN_EXAMPLE
int temp;
#endif
参考文章
1、C/C++中extern关键字详解 http://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777431.html
2、C/C++ include 条件编译 extern及编译连接浅析 https://www.cnblogs.com/chjtao/p/4673224.html