一、static修饰普通函数
在普通函数前加static,表明是局部函数,只是在当前源文件中有用,和局部函数相对的是全局函数(前面加extern就是全局函数,但一般省略),一般函数都是默认声明为全局函数的,并且省略extern。
以下面这个例子就可以清除的理解局部函数只在当前源文件中使用的意义了。
/*list.h*/
#include "iostream"
using namespace std;
/*static*/ void show()//注释①
{
cout << "show";
return;
}
/*a.cpp*/
#include "head.h"
int show_test()
{
show();
return 1;
}
/*main.cpp*/
#include <iostream>
#include "head.h"
using namespace std;
int main(void){
show();
system("pause");
}
如果在注释①处不加static,那么这个程序在编译时会显示重定义问题。
而加了static,就不会报错,原因如下:
普通static函数只能在本源文件中调用,(注意:而在VS中,头文件并不算源文件,如果没有被cpp文件调用,是不会被编译的。)所以头文件只有在被调用时,会被复制到cpp文件中。具体到程序中show函数分别被a.cpp和main.cpp两个源文件复制并被调用。
但由于static的特性,所以只能在各自的cpp文件中调用,也就是说,其实最后编译以后有两个show函数,他们都是局部show函数,只在各自cpp文件中才起作用。所以加了static的程序是编译通过的。
但是如果把static去掉,那么就会出现两个cpp文件中都定义了一个show函数,并且这个show函数是全局的,所以会显示重定义。
二、extern修饰普通函数
extern用在普通函数上,分为两种情况,一种是声明一个全局函数,可以省略,另外一个是告诉编译器,这个函数不在本源文件中,需要你去其他源文件中寻找对应的定义。也就是调用其他源文件中的全局函数。
如果一中的例子不想使用局部函数,那么可以让show变为全局函数,并且一个源文件调用show的头文件,另外一个源文件则使用extern show,程序如下:
/*list.h*/
#include "iostream"
using namespace std;
void show()
{
cout << "show";
return;
}
/*a.cpp*/
#include "head.h"
extern int show_test()//注释①,这里的extern只是声明一个全局函数,其实加不加都一样
{
show();
return 1;
}
/*main.cpp*/
#include <iostream>
//#include "head.h"//show函数由于没有static,所以只能在一个源文件中定义
using namespace std;
extern void show();//引用其他源文件中的全局函数,extern必须要加
int main(void){
show();
system("pause");
}
三、static修饰普通变量
普通变量在定义时加上static关键词,这个变量就变成了静态变量了。静态变量有如下特性
①存储在全局/静态存储区,在一个程序中,静态变量只可以有一份,并且生命周期和程序的生命周期一致,所以在程序运行过程中不会被销毁,尤其是静态局部变量也具有这一特性,(其实这一特性对于静态全局变量来说并没有用,因为全局变量同样存储在全局/静态存储区)如下程序为例:
#include <iostream>
using namespace std;
void show()
{
static int a=20;//注释①,虽然是局部变量,但是当这个函数执行结束以后,并不会消失,而是继续存储在全局/静态存储区
cout<<a++<<endl;
}
int main()
{
show();
show();
show();//可以看到a的值在不断往上叠加,而不是重新初始化
return 0;
}
结果如下:
②静态变量只会初始化一次,无论是局部还是全局,个人理解其中原理:
静态变量在编译阶段,如果初始化了,就会在静态变量区分配空间(这是确定的),但没有初始化,就会放入.bss段,并且不占用磁盘空间,在加载程序时,才会分配空间,并且将值置为默认值0。并且无论是否初始化,静态变量会多一个标志位。所以当程序执行过程中,如果执行到特性①程序中的注释①处,会判断是否被初始化,如果标志位是0,就没有初始化,就执行初始化函数,如果标志位是1,就初始化了,就跳过这一函数。可以参考博客:https://www.jianshu.com/p/d092b69d7184。
③对于普通的静态变量,可以不用初始化。那么这个静态变量默认是0或者NULL
④静态全局变量只能够在本源文件中使用,这一点和静态函数一样,也就是说在其他源文件调用extern 这个静态变量,会显示未定义(也就是静态变量就不能在其他源文件中使用extern声明符了)。
⑤静态局部变量可以在一个程序中不同的代码段定义相同的变量,但是存储到静态变量区时是不同的变量,程序如下:
#include <iostream>
using namespace std;
void show(int flag)
{
if (flag==0)
{
static int a=20;
cout<<a++<<endl;
}
else
{
static int a=10;
cout<<a++<<endl;
}
}
int main()
{
show(0);
show(1);
show(2);
show(0);
return 0;
}
结果如图所示:
可以看到两个局部静态变量a是不同的变量。
四、extern修饰普通变量
extern修饰变量的作用就是表明这句话是声明一个变量,而不是定义一个变量。
声明变量:是为了让名字被程序所知道,一个源文件想要使用别处定义的名字就必须包含对那个名字的声明
定义变量:是负责创建与名字关联的实体
注意,如果既使用extern,又对变量初始化,那么就不是声明,而是定义了,所以如下的情况是错误的:
/*a.cpp*/
int a=10;
/*main.cpp*/
#include <iostream>
using namespace std;
extern int a=20;//注释1
int main(void){
cout<<a;
}
会出现如下错误:
也就是重定义变量a了,而如果把注释1处的初始化=20去掉,就对了。
五、static修饰类成员变量
静态类成员变量有如下的特性:
- 静态成员变量属于整个类所有
- 静态成员变量的生命期不依赖于任何对象,为程序的生命周期
- 可以通过类名直接访问公有静态成员变量
- 所有对象共享类的静态成员变量
- 可以通过对象名访问公有静态成员变量
- 静态成员变量需要在类外单独分配空间(也就是不是和普通成员变量存储在一起的)
- 静态成员变量在程序内部位于全局/静态数据区
具体程序例子可以查看我的博客https://blog.csdn.net/qq_34489443/article/details/89104005
注意:
- const static成员变量是一种非常特殊的成员变量,对于这种成员变量的赋值有两种方法,类内定义(只能是系统内置类型),类外定义(不能用初始化列表,静态成员变量都不可以使用初始化列表)
- const成员变量只可以使用初始化列表进行初始化
- static成员变量不可以在类内初始化
具体的例子可以查看博客:https://www.cnblogs.com/xiezhw3/p/4354601.html,尤其注意其中对于定义和声明的讨论。
六、static修饰类成员函数
类的成员函数有如下特性:
- 静态成员函数是类的一个特殊的成员函数
- 静态成员函数属于整个类所有,没有this指针
- 静态成员函数只能直接访问静态成员变量和静态成员函数
- 可以通过类名直接访问类的公有静态成员函数
- 可以通过对象名访问类的公有静态成员函数
- 定义静态成员函数,直接使用static关键字修饰即可
编译器实现的具体原理,可以参考我的博客中的第二点:https://blog.csdn.net/qq_34489443/article/details/102502830
七、extern修饰类成员
这是非法的,类中成员,无论是变量还是函数,都不可以用extern来修饰。