C/C++中的static关键字详解

static是 C/C++中的关键字之一,是常见的函数与变量(C++中还包括类)的修饰符,它常被用来控制变量的存储方式和作用范围。 在众多高级语言中都有其作为关键字或函数出现,所以这也是应当被程序员熟知其各种含义的一个单词

C/C++中的static关键字详解_第1张图片

我们知道在函数内部定义的变量,当程序执行到它的定义处时,编译器为它在栈上分配空间,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想到的方法是定义为全局的变量,但定义一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅只受此函数控制)。static 关键字则可以很好的解决这个问题。

另外,在 C++ 中,需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见时,可将其定义为静态数据。

C/C++中的static关键字详解_第2张图片

C/C++ 中的 static

这里 static 作用主要影响着变量或函数的生命周期作用域,以及存储位置

1. 静态局部变量

定义在函数内部的变量称为局部变量(Local Variable),它的作用域仅限于函数内部, 离开该函数后就是无效的

当 static 修饰局部变量时:

  • 变量的存储区域由变为静态常量区
  • 变量的生命周期由局部变为全局
  • 变量的作用域不变。

函数调用开辟栈帧,函数中的局部变量在栈上分配存储空间,当函数执行完毕,函数栈帧销毁,栈空间由系统回收

而在static修饰函数局部变量的时,其修饰的静态局部变量只执行初始化一次,延长了局部变量的生命周期,直到程序运行结束以后才释放,但不改变作用域。

下面用代码进行验证:

#include 
 void fun()
{
	static int val = 0;   //static 修饰局部变量
	val++;
	printf("%d\n", val);
}
int main()
{
	for (int i = 0; i < 7; i++){
		fun();
	}
	return 0;
}

没有 static 时, 函数每调用一次, 变量就会进行一次初始化值为 0,

当由 static修饰时, 初始化语句只会被执行一次所以值会一直累加。

C/C++中的static关键字详解_第3张图片

2. 静态全局变量

在所有函数外部定义的变量称为全局变量(Global Variable),它的作用域默认是整个程序,也就是所有的源文件,包括 .c 和 .h 文件。

当 static 修饰全局变量时:

  • 变量的存储区域在全局数据区的静态常量区
  • 变量的作用域由整个程序变为当前文件。(extern声明也不行)
  • 变量的生命周期不变。

一个全局变量被 static 修饰,使全局变量只能在定义变量的当前文件使用,不能在其余文件使用,即使 extern外部声明也不行。

原因: 属于文件作用域的声明在缺省的情况下为 external 链接属性, 如定义个全局变量int g_a = 1;a的链接属性为external,而加上 static会修改变量的缺省链接属性,改为internal

声明了全局变量 g_a 和 g_b (具有 external 链接属性 )的其他源文件在使用这两个变量时实际访问的是生命与此处的这两个变量;但是 g_c 只能由这个源文件访问,因为链接属性**为internal

int g_a = 1;
extern int g_b;
static int g_c;

代码验证:

// add.c
static int global_val = 27;   //static 修饰全局变量
 //staticdemo1.c
extern global_val;
 int main()
{
	printf("%d", global_val);
	return 0;
}

不用 static 修饰 global_val 时的结果

C/C++中的static关键字详解_第4张图片

而使用 static 修饰时,链接时就会出现链接错误无法执行。

在这里插入图片描述

全局变量 与 extren

具有 extrenal 链接属性的实体在其他语言术语中称作全局实体(global entity ),所有源文件中的函数均可以访问它。只要变量并非声明与代码块或者函数定义内部,它在缺省的情况下链接属性即为 extrenal。如果一个变量声明与代码块内部,在它面前添加 extren 关键字将使它使它所引用的是全局变量而非局部变量。

具有链接属性为 extrenal 的实体总是具有静态存储类型。 全局变量在程序开始执行前创建,并在整个执行过程中始终存在。从属于函数的局部变量在函数在函数开始执行时进行创建,在函数执行完毕后销毁,但用于执行函数的机器指令在程序生命周期内一直存在。

使用 extren 进行声明提高代码的可读性是良好的编程习惯。

3. static 修饰函数

函数的作用域与全局变量一样都是整个程序。

当 static 修饰函数时:

函数的作用域由整个程序变为当前文件。(extern声明也不行)

一个函数被 static 修饰,使函数只能在定义的源文件使用,不能在其余文件使用,即使 extern外部声明也不行。(同static 修饰全局变量)

如果我们将函数声明为 static,就会把它的链接属性从external,改为internal,这样将使得其他源文件不能访问这个函数;对于函数而言,存储类型不是问题,因为代码总是存储在只读的代码区中。

// add.c
static int add(int a, int b)
{
	return a + b;
}
 //staticdemo1.c
extern add(int a, int b);
 int main()
{
	printf("%d", add(10, 20));
	return 0;
}

这里直接看结果:

没有 static 修饰:

C/C++中的static关键字详解_第5张图片

被 static 修饰:报了与修饰全局变量时同样的链接错误。

在这里插入图片描述

C++的 static 成员

声明为static类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的 成员函数,称之为静态成员函数

注:静态的成员变量一定要在类外进行初始化

class A
{
public :
	A(){   //构造函数
		_count++;
	}
	A(const A& y){
		_count++;
	}
	static int GetCount(){    //静态成员函数
		return _count;
	}
public:      
	int a;
	//静态成员变量--》在类中的是声明要在类外进行定义
	static int _count;  
};
};
 int A::_count = 0;   //静态变量 _count 定义
 void TestA() {
	cout << A::GetCount() << endl;  // 类静态成员通过 类名::静态成员 来访问
	A a1, a2; 
	A a3(a1);
	cout << A::GetCount() << endl;
}

静态成员变量 

1.静态成员变量必须在类外进行定义定义时不用加 static ,类中只是声明

C/C++中的static关键字详解_第6张图片

2.静态成员变量为所有类对象所共享,并没有包含在具体的对象中。

所以并不影响 sizeof() 大小

3.静态成员变量的访问: 

  • 类名::静态成员变量名 
  • 对象.静态成员变量名。
cout << A::_count << endl;
cout << a1._count << endl;

C/C++中的static关键字详解_第7张图片

类的对象可以使用静态成员函数和非静态成员函数。

注:静态成员变量也受访问限定符(public、protected、private)的限制。 所以私有的仍要通过类成员函数接口来进行访问,可以在通过类中公有的成员函数进行访问,

cout << A::GetCount() << endl;

但这种方式调用获取静态成员变量必须由静态成员函数访问,不能通过类名来调用类的非静态成员函数,否则就会出错

C/C++中的static关键字详解_第8张图片

静态成员函数

1.静态成员函数没有隐藏的 this 指针,不能访问非静态成员(变量、 函数)!

在这里插入图片描述

因为静态成员函数没有隐藏的 this 指针所以也不能定义成const成员函数(const 本质就是修饰隐藏参数this )

C/C++中的static关键字详解_第9张图片

2.静态成员函数不能调用非静态成员函数。

C/C++中的static关键字详解_第10张图片

3.非静态成员函数可以调用静态成员函数。

static void fun(){
		_count = 0;
	}
	int GetCount(){
		//cout << this << endl;    
		fun();
		return _count;
	}

总结:

(1) 静态成员变量使用前必须先初始化(在类外定义),如:int A::_count = 0;

(2) 静态成员变量为所有类对象所共享,也受访问限定符(public、protected、private)的限制

(3) 静态成员函不能调用非静态成员函数,非静态成员函数可以调用静态成员函数

(4) 静态成员函数没有隐藏的 this 指针

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!

你可能感兴趣的:(C/C++中的static关键字详解)