C语言关键字static与extern的详细解释

参考原文地址(static):https://blog.csdn.net/keyeagle/article/details/6708077

参考原文地址(extern):https://www.cnblogs.com/Lunais/p/5991135.html

C语言中的static与extern是C语言32个关键字中的比较重要的两个内容,也是我近期在学习C++过程中理解较为晦涩的部分,为此,参考了一些优质资料,在此做个总结。

C语言代码是以文件为单位来组织的,在一个源程序的所有源文件中,一个外部变量(注意不是局部变量)或者函数只能在一个源程序中定义一次,如果有重复定义的话编译器就会报错。伴随着不同源文件变量和函数之间的相互引用以及相互独立的关系,产生了extern和static关键字。

修饰符extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用”

一、static修饰全局变量

  1. 一个程序在内存中的布局:栈(局部变量,动态分配)、堆(用户自行申请、释放,通过指针访问)、bss段(存放进程中未初始化的全局变量)、data段(存放进程中初始化的全局变量)、text段(存放程序执行的二进制文件)、其他段(未知)。

  2. 静态全局变量:当进程中一个全局变量被static修饰后(被声明为static),则该全局变量被称为静态全局变量。

静态全局变量的存储位置不变,在bss段(未初始化)或者data段(初始化),但它的作用域是仅在它的源文件内,其它源文件都无法访问它。所以,当全局变量被static修饰后,就会限定在当前源文件(.c)内使用。

全局变量:隐式被static修饰的全局变量,作用域也是仅在它的源文件内,不能被其它源文件访问。但与静态全局变量(显式被static修饰的全局变量)不同的是全局变量在其它源文件中可以通过extern声明后访问,而静态全局变量则无法访问

file1.h文件,声明printer函数

//file1.h

void printer(void);

file1.c文件,定义了 静态全局变量name、全局变量address,以及printer函数

//file1.c
#include 
#include "file1.h"

static char* name = "keke";
char* address = "jiangxi";

void printer(void)
{
	printf("%s\n",name);
	printf("%s\n",address);
}

file2.c文件,可以成功调用printer函数,也可以通过extern声明访问全局变量address,但是无法访问name(即使被extern声明)

//file2.c
#include 
#include "file1.h"

//extern char* name;
extern char* address;

int main()
{
	printer();
	printf("%s\n",name);
	printf("%s\n",address);
}

二、static修饰局部变量

  1. 局部变量可以被存放在栈(动态分配,每次调用,位置都可能不同,作用域是局部)、堆(申请内存,要记得释放,作用域可以是整个源文件)当中。
  2. 静态局部变量:局部变量被static修饰后,则称为静态局部变量。静态局部变量则被存放在data段内(定义时,如果用户没有初始化,编译器会自动将其初始化为0),而且整个进程周期中,只定义和初始化一次,每次调用局部函数时,静态局部变量都会维持最后一次修改的值作用域是局部代码段

file3.c文件,在函数printer内分别定义了普通局部变量(i)和静态局部变量(j)。

//file3.c
#include 

void printer(void)
{
	int i;
	static int j;
	printf("i:%d j:%d\n",i++,j++);
}

int main()
{
	printer();
	printer();
	printer();
	printer();
	printer();
}

file.c文件执行结果如下:

i:-1218204059 j:0
i:-1218204058 j:1
i:-1218204057 j:2
i:-1218204056 j:3
i:-1218204055 j:4

可见,普通局部变量(i)每次调用函数的访问的值都不一样,而且随机;静态局部变量,定义初始化为0,并每次访问都是上一次函数调用修改的值。

三、static修饰函数

  1. static函数:被关键字static修饰的函数。static函数作用域是源文件,即其他源文件无法调用该函数,类似C++中的private函数。而对于普通函数而言,其他源文件只要添加了该函数的头文件(.h),其他源文件就可以调用该函数。
  2. 当你的程序中有很多个源文件的时候,你肯定会让某个源文件只提供一些外界需要的接口,其他的函数可能是为了实现这些接口而编写,这些其他的函数你可能并不希望被外界(非本源文件)所看到,这时候就可以用static修饰这些“其他的函数”。

file4.h文件,声明了普通函数(func1)和静态函数(func2)

//file4.h
void func1(void);
static void func2(void);

file4.c文件,定义了普通函数(func1)和静态函数(func2)

//file4.c
#include 
#include "file4.h"

void func1(void)
{
	printf("普通函数\n");
	func2();
}
static void func2(void)
{
	printf("静态函数\n");
}

file5.c文件,通过添加file4.h,可以调用func1(),但无法调用func2()。但是,func1()可以调用func2()。

//file5.c
#include "file4.h"

int main()
{
	func1();
	//func2();
}

可见,静态函数只能被它的源文件调用,而无法被其它源文件调用(即使添加了对应的头文件)。

而且,static函数可以很好地解决不同源文件中函数同名的问题,因为一个源文件对于其他源文件中的static函数是不可见的。

四、extern修饰变量的声明

  1. extern声明变量:表明该变量在其他源文件里已经被定义,此处需要使用。extern声明的变量必须是在其他源文件内的非静态的全局变量(保证作用域不受限制,可以声明)。
  2. 举例在file1.h/file1.c/file2.c中。

五、extern修饰函数的声明

  1. extern声明函数:表明该函数在其他源文件里已经被定义,此处需要使用。extern声明的变量必须是在其他源文件内的非静态的函数(保证作用域不受限制)。
  2. 那么我们有两种方式可以其他源文件内的非静态函数,一种是添加该函数声明头文件(加载该头文件的全部函数,编译较慢),另一种是使用extern修饰(调用哪个函数,就声明哪个函数,编译较快,效率较高)。
  3. 举例在file4.h/file4.c/file5.c中。

 

 

 

 

 

 

 

 

你可能感兴趣的:(C,基本概念)