参考原文地址(static):/keyeagle/article/details/6708077
参考原文地址(extern):/Lunais/p/5991135.html
C语言中的static与extern是C语言32个关键字中的比较重要的两个内容,也是我近期在学习C++过程中理解较为晦涩的部分,为此,参考了一些优质资料,在此做个总结。
C语言代码是以文件为单位来组织的,在一个源程序的所有源文件中,一个外部变量(注意不是局部变量)或者函数只能在一个源程序中定义一次,如果有重复定义的话编译器就会报错。伴随着不同源文件变量和函数之间的相互引用以及相互独立的关系,产生了extern和static关键字。
修饰符extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用”。
一、static修饰全局变量
一个程序在内存中的布局:栈(局部变量,动态分配)、堆(用户自行申请、释放,通过指针访问)、bss段(存放进程中未初始化的全局变量)、data段(存放进程中初始化的全局变量)、text段(存放程序执行的二进制文件)、其他段(未知)。
静态全局变量:当进程中一个全局变量被static修饰后(被声明为static),则该全局变量被称为静态全局变量。
静态全局变量的存储位置不变,在bss段(未初始化)或者data段(初始化),但它的作用域是仅在它的源文件内,其它源文件都无法访问它。所以,当全局变量被static修饰后,就会限定在当前源文件(.c)内使用。
全局变量:隐式被static修饰的全局变量,作用域也是仅在它的源文件内,不能被其它源文件访问。但与静态全局变量(显式被static修饰的全局变量)不同的是全局变量在其它源文件中可以通过extern声明后访问,而静态全局变量则无法访问。
file1.h文件,声明printer函数
//file1.hvoid printer(void);
file1.c文件,定义了 静态全局变量name、全局变量address,以及printer函数
//file1.c#include <stdio.h>#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 <stdio.h>#include "file1.h"//extern char* name;extern char* address;int main(){printer();printf("%s\n",name);printf("%s\n",address);}
二、static修饰局部变量
局部变量可以被存放在栈(动态分配,每次调用,位置都可能不同,作用域是局部)、堆(申请内存,要记得释放,作用域可以是整个源文件)当中。
静态局部变量:局部变量被static修饰后,则称为静态局部变量。静态局部变量则被存放在data段内(定义时,如果用户没有初始化,编译器会自动将其初始化为0),而且整个进程周期中,只定义和初始化一次,每次调用局部函数时,静态局部变量都会维持最后一次修改的值,作用域是局部代码段。
file3.c文件,在函数printer内分别定义了普通局部变量(i)和静态局部变量(j)。
//file3.c#include <stdio.h>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:0i:-1218204058 j:1i:-1218204057 j:2i:-1218204056 j:3i:-1218204055 j:4
可见,普通局部变量(i)每次调用函数的访问的值都不一样,而且随机;静态局部变量,定义初始化为0,并每次访问都是上一次函数调用修改的值。
三、static修饰函数
static函数:被关键字static修饰的函数。static函数作用域是源文件,即其他源文件无法调用该函数,类似C++中的private函数。而对于普通函数而言,其他源文件只要添加了该函数的头文件(.h),其他源文件就可以调用该函数。
当你的程序中有很多个源文件的时候,你肯定会让某个源文件只提供一些外界需要的接口,其他的函数可能是为了实现这些接口而编写,这些其他的函数你可能并不希望被外界(非本源文件)所看到,这时候就可以用static修饰这些“其他的函数”。
file4.h文件,声明了普通函数(func1)和静态函数(func2)
/file4.hvoid func1(void);static void func2(void);
file4.c文件,定义了普通函数(func1)和静态函数(func2)
/file4.c#include <stdio.h>#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修饰变量的声明
extern声明变量:表明该变量在其他源文件里已经被定义,此处需要使用。extern声明的变量必须是在其他源文件内的非静态的全局变量(保证作用域不受限制,可以声明)。
举例在file1.h/file1.c/file2.c中。
五、extern修饰函数的声明
extern声明函数:表明该函数在其他源文件里已经被定义,此处需要使用。extern声明的变量必须是在其他源文件内的非静态的函数(保证作用域不受限制)。
那么我们有两种方式可以其他源文件内的非静态函数,一种是添加该函数声明头文件(加载该头文件的全部函数,编译较慢),另一种是使用extern修饰(调用哪个函数,就声明哪个函数,编译较快,效率较高)。
举例在file4.h/file4.c/file5.c中。