我们知道,程序的编译单位是源程序文件,一个源文件可以包含一个或若干个函数。在函数内定义的变量是局部变量,而在函数之外定义的变量则称为外部变量,外部变量也就是我们所讲的全局变量。它的存储方式为静态存储,其生存周期为整个程序的生存周期。全局变量可以为本文件中的其他函数所共用,它的有效范围为从定义变量的位置开始到本源文件结束。

1 一个文件引用另一个文件全局变量

如果整个工程由多个源文件组成,在一个源文件中想引用另外一个源文件中已经定义的外部变量,只需在引用变量的文件中用 extern 关键字加以声明即可
在这里插入图片描述

举例
在这里插入图片描述

在这里插入图片描述
test1.cpp

#include<stdio.h>
int kpi = 100;

int getmax(int a, int b) {
	return a > b ? a : b;
}

test2.cpp

#include<stdio.h>
int main() {

	extern int kpi;//外部变量申明
	extern int getmax(int a, int b);//外部函数申明

	int i = 0;
	i = kpi;
	printf("%d",i);

	int max = getmax(5,10);
	printf("\n%d", max);
}

如果不使用extern 就会报错
在这里插入图片描述

在这里插入图片描述

2 在定义点之前的函数想引用该全局变量

如果全局变量不在文件的开头定义,有效的作用范围将只限于其定义处到文件结束。如果在定义点之前的函数想引用该全局变量,则应该在引用之前用关键字 extern 对该变量作“外部变量声明”,表示该变量是一个已经定义的外部变量。有了此声明,就可以从“声明”处起,合法地使用该外部变量。

#include <stdio.h>
int max(int x,int y);
int main(void)
{
    int result;
    /*外部变量声明*/
    extern int g_X;
    extern int g_Y;
    result = max(g_X,g_Y);
    printf("the max value is %d\n",result);
    return 0;
}
/*定义两个全局变量*/
int g_X = 10;
int g_Y = 20;
int max(int x, int y)
{
    return (x>y ? x : y);
}

在这里插入图片描述
代码中,全局变量 g_X 与 g_Y 是在 main 函数之后声明的,因此它的作用范围不在 main 函数中。如果我们需要在 main 函数中调用它们,就必须使用 extern 来对变量 g_X 与 g_Y 作“外部变量声明”,以扩展全局变量的作用域。也就是说,如果在变量定义之前要使用该变量,则应在使用之前加 extern 声明变量,使作用域扩展到从声明开始到本文件结束。

3 c语言中extern关键字

c语言中extern关键字

extern关键字适用于变量及函数,并且扩展了他们的可见性,这也就是它被命名为extern的原因。
首先介绍一下声明(declaration)和定义(definition)的区别:

声明一个变量或者函数并没有给它们分配内存,它只是告诉程序它的类型是什么。在函数声明的情况下,它还告诉程序参数、它们的数据类型、这些参数的顺序以及函数的返回类型。

定义一个变量或者函数时,除了声明所做的一切之外,他还为该变量或函数分配内存。因此,我们可以把定义看作时声明的超集(或者声明是定义的子集)。
一个变量或者函数可以声明任意次数,但只能定义一次。

当我们在声明或定义函数时,会隐藏extern关键字。例如,当我们定义如下函数时:

int foo(int arg1, char arg2);

编译器会将其视为

extern int foo(int arg1, char arg2);

由于extend关键字将函数的可见性扩展到整个程序,因此可以在整个程序的任何文件中调用该函数,前提是这些文件包含该函数的声明。有了函数的声明,编译器就知道函数的定义存在于其他地方,然后继续编译文件。

当我们需要对一个变量声明而不定义的时候,我们会用extern来修饰该变量。如下所示

extern int var;//声明了一个名为var的整型变量(并没有定义,也就是说没有给var分配内存)

这里声明了一个名为var的整型变量(并没有定义,也就是说没有给var分配内存)。这个声明可以进行多次。但是当我们去掉extern关键字后:

int var;//声明并定义了一个名为var的整数类型变量,var被分配内存

在这一行中,声明并定义了一个名为var的整数类型变量,var被分配内存。

因此当我们声明一个函数时,extern关键字是隐式的,当我们想声明而不定义变量时,我们需要显式的包含extern关键字。此外,由于extern关键字扩展了整个程序的可见性,通过对变量使用extern关键字,我们可以在程序中任何地方使用该变量,前提是我们包含了该变量在某个地方的声明。

接下来用几个例子来理解extern关键字:

3.1 Example 1:

int var;
int main(void) {
	var = 10;
	return 0;
}

这个程序是可以成功运行的,因为var变量是全局变量,并且声明和定义的。

3.2 Example 2:

extern int var;
int main(void) {
	return 0;
}

这个程序也是可以成功运行的,因为这里虽然只是声明了var变量,但是var变量并没有实际使用过,所以不会出现问题。

3.3 Example 3:

extern int var;
int main(void) {
	var = 10;
	return 0;
}

这个程序就会出现问题,会在编译的时候抛出错误。因为var变量只进行了声明,并没有定义(也就是说没有分配内存),程序试图将不存在的变量的值更改为10。

3.4 Example 4:

#include "somefile.h"
extern int var;
int main(void) {
	var = 10;
	return 0;
}

假定somefile.h包含var的定义,这个程序是可以成功运行的。

3. 5 Example 5:

extern int var = 0;
int main(void) {
	var = 10;
	return 0;
}

如果用extern声明了var变量,并且对该变量进行初始化,那么该变量被认为是已定义的。所以该程序可以顺利编译并运行。

3.6 总结

声明可以进行任意次,但是定义只能一次
extern关键字用于扩展变量/函数的可见性
extern修饰函数是隐式的,函数的声明或定义中默认使用extern修饰
extern修饰变量时,该变量只被声明,而没有定义
当使用extern修饰变量同时进行初始化时,该变量同时也被定义

4 C++导入C函数 extern “C”

C++中的extern “C”用法详解

有了符号的概念我们再来看extern “C”的用法就很容易了

extern "C"  

{  

    int func(int);  

    int var;  

}  

它的意思就是告诉编译器将extern “C”后面的括号里的代码当做C代码来处理,当然我们也可以以单条语句来声明

extern "C" int func(int);  

extern "C" int var;  

这样就声明了C类型的func和var。很多时候我们写一个头文件声明了一些C语言的函数,而这些函数可能被C和C++代码调用,当我们提供给C++代码调用时,需要在头文件里加extern “C”,否则C++编译的时候会找不到符号,而给C代码调用时又不能加extern “C”,因为C是不支持这样的语法的,常见的处理方式是这样的,我们以C的库函数memset为例

#ifdef __cplusplus 
extern "C" { 
#endif 
 
void *memset(void*, int, size_t); 
 
#ifdef __cplusplus 
} 
#endif 

其中__cplusplus是C++编译器定义的一个宏,如果这份代码和C++一起编译,那么memset会在extern "C"里被声明,如果是和C代码一起编译则直接声明,由于__cplusplus没有被定义,所以也不会有语法错误。这样的技巧在系统头文件里经常被用到。

5 其他相关文章

文尾附相关文章
在这里插入图片描述

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐