面阿里的时候被问了这个关键字的用法,问题很简单,但是当时答得一般,故总结一番。
首先说static,表示静态,这是一个C语言里面就有的关键字,C++中仍旧沿用并做了些许扩展。
在解释static关键字之前,先了解一下程序的内存布局。
内存布局
一个程序本质上包含bss段、data段和text段。
bss段通常存放程序中未初始化的变量,如全局变量和静态变量,在编译器编译时分配,系统释放;
data段则用来存放程序中已经初始化的变量,包括堆(heap)、栈(stack),以及一些全局变量、静态变量和常量。
而text段则用来存放程序执行的代码,并且通常为只读的。
堆和栈
给一个列表对比两者区别吧。
名称 | 堆 | 栈 |
---|---|---|
申请方式 | 程序员动态申请 | 系统自动分配 |
释放方式 | 程序员释放,否则由OS回收 | 系统自动释放 |
系统响应 | 遍历一个记录空闲地址的链表,返回堆节点 | 栈空间足够大时提供内存,否则报栈溢出 |
大小限制 | 较大较灵活(有效的虚拟内存都可以) | 较小而且受限(栈空间大小固定) |
申请效率 | 相对较慢 | 速度快 |
存储内容 | 程序员自行安排 | 下一条指令地址>参数>局部变量 |
扩展结构 | 高地址扩展结构 | 低地址扩展结构,由专门的指令完成压栈出栈 |
继续说static关键字,它到底可以修饰什么,有什么作用呢?
全局变量
用static修饰全局变量,该变量就成为一个静态全局变量。
#include<iostream>
using namespace std;
int m; //定义全局变量
static int n; //定义静态全局变量
int main(void)
{
n = 20;
m = 30;
cout<<n<<" "<<m<<endl;
return 0;
}
m被定义为全局变量,n为静态全局变量,它们都是在全局数据区分配内存,但是全局变量是整个程序可见的,而静态全局变量则仅在声明它的文件可见,在文件之外是不可见 。定义全局变量可以实现变量在所有源文件之间的共享,而定义静态全局变量,一方面可以隐藏该变量,另一方面其他文件中也可以出现同名定义而不会导致冲突。
总之,static修饰全局变量,改变了这个变量的作用域。
局部变量
用static修饰局部变量,该变量就成为一个静态局部变量。
通常函数体内定义的一个局部变量,每当程序运行到变量定义语时,才会为该局部变量分配栈空间,随着程序退出函数体,系统会回收栈内存,局部变量也相应被释放。而静态局部变量同静态全局变量一样,存放于全局数据区,只被初始化一次,而且全局数据区的数据不会因为函数退出而释放空间,也即下一次调用时变量的值不会被重置。
总结,static修饰局部变量,改变了这个变量的内存存储位置,并且改变了它的生命周期。
函数
用static修饰函数,该函数则被定义为静态函数。
静态函数同静态全局变量一样,只在声明它的文件中可见,而不能被其它文件使用。
static修饰函数,改变了这个函数的作用域。
类成员变量
用static修饰类的成员变量,这个变量就是类内的静态数据成员。
对于非静态数据成员,每个类对象都会有一份拷贝,而静态数据成员则为该类的所有实例共享,无论定义了多少个类对象,静态数据成员在程序中只有一份拷贝,由所有对象共享访问。静态数据成员存储在全局数据区,只分配一次,供所有对象公用。
注意:
1、静态数据成员为该类的所有实例共享,任何类实例修改了该变量,对其他实例均可见;
2、静态数据成员定义时需要分配空间,因此不能在类声明时定义;
3、静态数据成员和普通成员一样遵循public、protected、private访问规则。
总结,static修饰类成员变量,节省存储空间,并且由所有对象共享。
类成员函数
用static修饰类的成员函数,这个函数就是类内的静态成员函数。
普通类成员函数一般都隐含了一个this指针,指向类对象本身。而类的静态成员函数与静态数据成员一样,为整个类所有,因此它没有this指针,因此它也无法访问非静态数据成员或非静态成员函数。
注意:
1、非静态成员可以访问静态成员,但是静态成员只能访问静态成员。
2、静态成员函数没有this指针的开销,相比类全局函数速度上会稍快。