一. extern 关键字
利用关键字extern,可以在一个文件中引用另一个文件中定义的变量或者函数,
1、引用同一个文件中的变量
#include<stdio.h>
int func();
int main()
{
func(); //1
printf("%d",num); //2
return 0;
}
int num = 3;
int func()
{
printf("%d\n",num);
}
按照这个顺序,变量 num在main函数的后边进行声明和初始化的话,那么在main函数中是不能直接引用num这个变量的。
因为当编译器编译到这一句话的时候,找不到num这个变量的声明。
但是在func函数中是可以正常使用,因为func对num的调用是发生在num的声明和初始化之后。
如果我不想改变num的声明的位置,但是想在main函数中直接使用num这个变量,怎么办呢?可以使用extern这个关键字。
利用extern关键字先声明一下num变量,告诉编译器num这个变量是存在的,但是不是在这之前声明的,你到别的地方找找吧,果然,这样就可以顺利通过编译啦。
下面的程序就是利用extern关键字,使用在后边定义的变量。
#include<stdio.h>
int func();
int main()
{
func(); //1
extern int num;
printf("%d",num); //2
return 0;
}
int num = 3;
int func()
{
printf("%d\n",num);
}
2、引用另一个文件中的变量
extern这个关键字的真正的作用是引用不在同一个文件中的变量或者函数。
main.c
#include<stdio.h>
int main()
{
extern int num;
printf("%d",num);
return 0;
}
b.c
#include<stdio.h>
int num = 5;
void func()
{
printf("fun in a.c");
}
例如,这里b.c中定义了一个变量num,如果main.c中想要引用这个变量,那么可以使用extern这个关键字.
注意只有当一个变量是一个全局变量时,extern变量才会起作用。
注意:
- extern 关键字实质上是声明,别的地方必须实现定义。
- extern关键字只需要指明类型和变量名就行了,不能再重新赋值,初始化需要在声明处进行,像这种写法是不行的。
extern int num=4;
- 但是在声明之后就可以使用变量名进行修改了,像这样:
#include<stdio.h>
int main()
{
extern int num;
num=1;
printf("%d",num);
return 0;
}
使用include将另一个文件全部包含进去可以引用另一个文件中的变量,但是这样做的结果就是,被包含的文件中的所有的变量和方法都可以被这个文件使用,这样就变得不安全,如果只是希望一个文件使用另一个文件中的某个变量还是使用extern关键字更好。
3、引用另一个文件中的函数
extern除了引用另一个文件中的变量外,还可以引用另一个文件中的函数,引用方法和引用变量相似。
main.c
#include<stdio.h>
int main()
{
extern void func();
func();
return 0;
}
b.c
#include<stdio.h>
const int num=5;
void func()
{
printf("fun in a.c");
}
3. extern 和 static
- extern 表明该变量在别的地方已经定义过了,在这里要使用那个变量.
- static 表示静态的变量,分配内存的时候, 存储在静态区,不存储在栈上面.
首先,static与extern是一对“水火不容”的家伙,也就是说extern和static不能同时修饰一个变量;
其次,static修饰的全局变量声明与定义同时进行,也就是说当你在头文件中使用static声明了全局变量后,它也同时被定义了;
最后,static修饰全局变量的作用域只能是本身的编译单元,也就是说它的“全局”只对本编译单元有效,其他编译单元则看不到它,如:
(1) test1.h:
#ifndef TEST1H
#define TEST1H
static char g_str[] = "123456";
void fun1();
#endif
(2) test1.cpp:
#include "test1.h"
void fun1() { cout << g_str << endl; }
(3) test2.cpp
#include "test1.h"
void fun2() { cout << g_str << endl; }
以上两个编译单元可以连接成功, 当你打开test1.obj时,你可以在它里面找到字符串"123456",同时你也可以在test2.obj中找到它们。
它们之所以可以连接成功而没有报重复定义的错误是因为虽然它们有相同的内容,但是存储的物理地址并不一样,就像是两个不同变量赋了相同的值一样,而这两个变量分别作用于它们各自的编译单元。
(1) test1.cpp:
#include "test1.h"
void fun1()
{
g_str[0] = ''a'';
cout << g_str << endl;
}
(2) test2.cpp
#include "test1.h"
void fun2() { cout << g_str << endl; }
(3) void main() {
fun1(); // a23456
fun2(); // 123456
}
这个时候你在跟踪代码时,就会发现两个编译单元中的g_str地址并不相同,因为你在一处修改了它,在内存中存在了两份拷贝给两个模块中的变量使用。
5. extern 和const
- C++中const修饰的全局常量据有跟static相同的特性,即它们只能作用于本编译模块中,
- **但是const可以与extern连用来声明该常量可以作用于其他编译模块中, 如extern const char g_str[];**然后在原文件中别忘了定义: const char g_str[] = “123456”;
所以当const单独使用时它就与static相同,
而当与extern一起合作的时候,它的特性就跟extern的一样了!
参考https://blog.csdn.net/xingjiarong/article/details/47656339 和 https://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777431.html