C/CPP : static
本文由zaccur整理编辑,如需转载请注明出处。
目录
综述
static关键字是C, C++中都存在的关键字。从字面理解是“静态的“的意思,与此相对应的,应该是“动态的“。
static的作用主要有以下3个:
- 局部数据:扩展生存期(C/CPP);
- 全局数据/函数:限制作用域(C/CPP);
- 类成员数据/函数:数据唯一性(ONLY CPP);
局部数据:扩展生存期( C/CPP)
此作用主要是针对局部变量 来说。
局部变量 | 在C/C++中, 针对局部变量 按照存储形式可分为三种关键字修饰auto, static, register |
auto | 默认缺省的修饰类型,即普通的局部变量 内存分配:栈中分配内存; 生命周期:由程序控制,通常在进入局部作用域定义时被分配,离开局部作用域时被释放; 初始化:自动对象的值是任意的,除非他被显示初始化; 作用域:作用域为局部作用域,当定义它的函数或者语句块结束的时候,作用域随之结束。 |
register | CPU寄存器中存储,基本类同auto,相比auto而言,节省内存,提高效率; |
static | static修饰的局部变量,即 局部静态变量 内存分配:静态存储区分配(静态存储区在整个程序运行期间都存在); 生命周期:生命周期和程序一样,在离开作用域之后,并没被销毁,仍然驻留在内存直到程序结束; 初始化:所处模块初次运行时初始化仅且只一次,未经初始化的静态变量会被程序自动初始化为0; 作用域:作用域为局部作用域,当定义它的函数或者语句块结束的时候,作用域随之结束。 |
举例:
static是按照程序的生命周期来分配释放变量的,如果在main前设置断点,然后查看static变量,已经被初始化,也就是说static在执行main函数前已经被初始化。也就是在程序初始化时被分配。
引申:在程序中,常用内存类型主要有堆、栈和静态存储区。
要理解static局部变量就必须首先理解这三种内存类型。
堆 | 由程序员自己分配释放(用malloc和free,或new和delete) ,如果我们不手动释放,那就要到程序结束才释放。如果对分配的空间在不用的时候不释放而一味的分配,那么可能会引起内存泄漏,其容量取决于虚拟内存,较大。 |
栈 | 由编译器自动分配释放,其中存放在主调函数中被调函数的下一句代码、函数参数和局部变量,容量有限,较小。 |
静态存储区 | 由在编译时由编译器分配,由系统释放,其中存放的是全局变量、static变量和常量. |
主要区别:
1) 堆是由低地址向高地址扩展,栈是由高地址向低地址扩展。
2) 堆是不连续的空间,栈是连续的空间。
3) 在申请空间后,栈的分配要比堆的快。对于堆,先遍历存放空闲存储地址的链表、修改链表、再进行分配;对于栈,只要剩下的可用空间足够,就可分配到,如果不够,那么就会报告栈溢出。
4) 栈的生命期最短,到函数调用结束时;静态存储区的生命期最长,到程序结束时;
堆中的生命期是到被我们手动释放时(如果整个过程中都不手动释放,那就到程序结束时)。
引申:存储说明符auto,register,extern,static的存储期
存储期 |
|
自动存储期 | auto和register对应自动存储期。具有自动存储期的变量在进入声明该变量的程序块时被建立,它在该程序块活动时存在,退出该程序块时撤销。初始化值任意,除非被显示初始化。 |
静态存储期 | 关键字extern和static用来说明具有静态存储期的变量和函数。用static声明的局部变量具有静态存储持续期(static storage duration),或静态范围(static extent)。虽然他的值在函数调用之间保持有效,但是其名字的可视性仍限制在其局部域内。静态局部对象在程序执行到该对象的声明处时被首次初始化。 |
全局数据/函数:限制作用域( C/CPP)
此作用主要针对全局变量 来说。
全局变量 | C/C++中, 不论是普通全局变量还是static全局变量,其存储区都是静态存储区,内存分配上无区别; 针对存储在静态数据区的变量,内存中所有的字节默认值都是0x00,会在程序刚开始运行时就完成唯一的一次初始化,如static int var;默认直接赋0; |
普通的全局变量和函数 | 其作用域为整个程序或项目,外部文件(其它cpp文件)可以通过extern关键字访问该变量和函数。 一般不提倡这种用法,如果要在多个cpp文件间共享数据,应该将数据在头文件声明为extern类型, 然后在其中任何一个包含该头文件的cpp中初始化(一次)就好,cpp文件中不需要添加extern关键字, 因为extern是声明性关键字,然后所有包含该头文件的cpp文件都可以访问相同的一个变量; |
static的全局变量和函数 | 其作用域为当前cpp文件,其它的cpp文件不能访问该变量和函数。(C语言中也可以理解为隐藏的作用) 变量: 如果有两个cpp文件声明了同名的全局静态变量,那么他们实质上是独立的两个同名的不同变量(指针地址不同),如果都只是引用,没有修改,编译器可能会优化为同一地址,反之不会。 例如: 头文件中的static变量如果在一个头文件中定义:static int givable = 0; 那么会为每个包含该头文件的cpp都创建一个全局变量,但他们都是独立的;所以也不建议这样的写法,因为只是创建了一组同名而不同作用域的多个变量。 函数: static函数的好处是不同的人编写不同的函数时,不用担心不同文件中同名函数而导致重复定义错误。 |
补充:在C语言中,static对于全局变量/函数的作用:隐藏。
当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。
举例说明。我们要同时编译两个源文件,一个是xxx.c,另一个是main.c。
//下面是xxx.c的内容 char g_var = 'U'; // global variable void msg() { printf("Hello\n"); } //下面是main.c的内容 int main(void) { (void)msg(); extern char g_var; // extern variable must be declared before use printf(" %c ", g_var); return 0; } /**************************************** * 结果: * Hello U ****************************************/ |
所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。
如果加了static,就会对其它源文件隐藏。
此例中,g_var是全局变量,msg是函数,并且都没有加static前缀,因此对于另外的源文件main.c是可见的。
例如在g_var和msg的定义前加上static,main.c就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏,而对于变量(局部变量),static还可以扩充其生命周期,同时初始化的时候直接默认赋值0(全局变量默认)。
类成员数据/函数:数据唯一性( ONLY CPP)
此作用是C++对static关键字的重用。主要针对类里静态成员数据/成员函数。
在这里面, static既不是限定作用域的, 也不是扩展生存期的作用, 而是指成员变量/函数在此类中的唯一性。
表示static成员变量和函数属于一个类而不是属于此类的任何特定对象的变量和函数.
这是与普通成员函数的最大区别, 比如在对某一个类的对象进行计数时,计数生成多少个类的实例,都可以用到静态数据成员。 (针对静态数据成员而言, 成员函数不管是否是static,在内存中只有一个副本,普通成员函数调用时,需要传入this指针,static成员函数调用时,没有this指针。)
static数据成员的初始化:
(1) 初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆。
(2) 初始化时不加该成员的访问权限控制符private,public等。
(3) 初始化时使用作用域运算符来标明它所属类,因此,静态数据成员是类的成员,而不是对象的成员。
(4) 静态数据成员是静态存储的,它是静态生存期,必须对它进行初始化。
static成员函数:
静态成员函数和静态数据成员一样,它们都属于类的静态成员,都不是任何对象的成员。
因此,对静态成员的引用不需要用对象名。
静态成员函数仅能访问静态的数据成员,不能访问非静态的数据成员,也不能访问非静态的成员函数,这是由于静态的成员函数没有this指针。
注意:本文由zaccur整理编辑,如需转载请注明出处。
参考:
c/c++ static 用法总结(三版本合一)来自 <https://blog.csdn.net/mznewfacer/article/details/6898005>
C/C++:多个.cpp文件包含同一个.h头文件定义方法 来自 <https://blog.csdn.net/zhanh1218/article/details/35637273>