首页 > 代码库 > static

static

Static 的作用

一、隐藏

当同时编译多个文件时,所有未加static前缀的全局变量和函数都有全局可见性。

1.不加static的两个源文件

               Static_1.c                                       static_2.c

        技术分享           技术分享 

技术分享

Static_2.c中可以使用stati_1.c中定义的全局变量a和函数msg(),因为所有未加static前缀的全局变量和函数都有全局可见性,其他的源文件都可以访问。

2.加上static的两个源文件

  static_1.c                                           static_2.c

 技术分享   技术分享

 技术分享

加了static之后,就会对其他源文件隐藏。

利用这样的特性可以在不同的文件中定义同名函数和同名变量,而不必担心重名冲突。

加了static之后,也可以防止变量被其他文件修改。

对函数来讲,static仅限于隐藏;对于变量,static还有如下作用。

二、保持变量内容的持久

共有两种变量存储在静态存储区:全局变量和static变量。Static变量可以控制变量的范围,改变了作用域。

存储在静态数据区的变量会在程序刚开始运行时完成初始化,也是唯一的一次初始化。

 技术分享

三、默认初始化为0

工作在静态数据存储区的变量初始化值都为0。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这个特点可以减少程序员的工作量。比如初始化一个系数矩阵,再比如把一个字符数组当成字符串来使用。

技术分享

 技术分享

静态全局变量 - 静态局部变量

全局变量、静态全局变量、静态局部变量都存储在静态存储区中;局部变量存储在栈中。

利用static修饰局部变量时,改变了局部变量的位置,从原来的栈中存放为静态存储区。但是静态局部变量在离开作用域后,并没有被销毁,而仍然留在内存中知道程序结束,只是不能再对它访问了,下次访问该函数时该静态局部变量扔保留原有的值。

 技术分享  

技术分享

Static修饰全局变量的时候,没有改变存放位置,只是改变了作用域(在声明它的其他文件之外是不可见的)。

静态函数(static函数或者内部函数)

这里的static不是指的是存储方式,而是只函数的作用域只能是该源程序。函数的定义和声明默认情况下是extern的,但是静态函数只是在声明定义它的文件中可见,不能被其他文件使用。(见程序例子)

静态函数的好处

其他文件中可以定义相同名字的函数;

静态函数不能被其他文件修改。

注:C语言代码是以文件为单位组织的(一个程序中有多个.c源程序),在一个源程序所有源文件当中,一个外部变量或者函数只能在某个文件中定义一次,而其他文件可以通过extern声明来访问它(定义外部变量或者函数的源文件中半酣对该外部变量的extern声明)。

 

可重入问题

函数是可重入的是指只要输入的数据相同,就应该产生相同的输出(不可预测)。下面的函数就是不可重入的:

 技术分享

技术分享

不可预测的原因就是函数中使用了static变量。如果需要一个可重入的函数,就需要避免在函数中使用static变量。

有时函数中必须要使用static变量,比如当某函数的返回类型为指针类型时,必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。

因此,设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题。

存储说明符

存储说明符auto,register,extern,static对应两种存储期:自动存储期和静态存储期。

auto和register对应自动存储期。具有自动存储期的变量在进入声明该变量的程序块时被建立,在该程序块活动时存在,退出该程序块时被撤销。

extern和static用来说明具有静态存储期的变量和函数。用static声明的局部变量具有静态存储持续期(静态范围)。虽然其值在函数调用期间保持有效,但其名字仍然限定在其局部域内。静态局部对象在程序执行到该对象声明处时被首次初始化。

结论

Static 为常见的修饰符,被用来控制存储方式和可见性。

Static对全局变量的修饰,可以认为是限制了只能是本文件引用此变量。有的程序是由很多.c文件构成,彼此可以相互引用变量,但是假如static修饰之后,只能被文件中函数引用此变量。

Static对栈变量修饰,可以认为将栈变量的生命周期延长到程序执行结束时(改为静态数据区)。一般来说,栈变量的生命周期由OS管理,在退栈过程中,栈变量的生命周期也就结束了。在加入static修饰之后变量在静态数据区中。同时,离开定义它的函数之后不能再使用,但如果再次调用定义它的函数时,他又可以继续使用,而且保存了前次被调用后留下的值。

static对函数的修饰和对全局变量相似,只能在本源文件中使用,而不能被同一个程序的其他文件使用。

Static有两个方面的特征:

变量会在全局存储区中,这样可以在下一次调用的时候还可以保留原来的赋值。这是和堆栈变量的区别。

变量用static告知编译器,自己仅仅在变量的作用范围内可见,这是和全局变量的区别。

定义在静态区内省去了函数入栈出栈的操作,速度更快一些。 

 

Static在面向对象中的使用(针对类)

静态数据成员

对于非静态数据成员,每个类对象都有自己的拷贝。而静态数据成员被当做类的拷贝。静态数据成员在程序中只有一份拷贝,由该类的左右对象访问。即:静态数据成员只分配一次内存,为所有对象共有。

静态数据成员存储在全局数据区内,因此它不属于类对象,在没有产生类对象时其作用域就可见,即在没有类实例时就可以操作它。

静态数据成员定义时要分配空间,所以不能再类声明中定义,只能在类外定义。(因为类声明并不为类分配内存。)静态数据成员也不能再累的外部接口的头文件中定义,因为那会造成在多个使用该类的原文件中对其重复定义。

静态数据成员和普通数据成员一样遵守public、protected、private访问规则。

静态数据成员初始化的格式为:<数据类型><类名>::<静态数据成员名>=<值>

类的静态数据成员的两种访问方式(如果其访问权限是public,如果为private仍然在类外定义,但是不能访问):

<类对象名>.<静态数据成员名>或者<类对象名>::<静态数据成员名>

静态数据成员主要用在各个对象都有相同的某项属性时。这样的好处是:不管定义多少个对象,都共享全局数据区的内存,节省的空间;修改简单,只修改一次。

同全局变量相比,使用静态数据成员的优势:静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其它全局名字冲突的可能性;可以实现信息隐藏。静态数据成员可以是private,但是全局不能。

Static数据成员只能在类外初始化,不能在类内,但是如果有const修饰,则可以在类内进行初始化,这和const的作用有关,因为const表示不可修改。

注:静态数据成员属于类,仅在全局区有一份拷贝供所有对象共享

定义方式

访问方式(和权限有关)

静态成员函数:

同静态数据成员一样,静态成员函数属于类定义的一部分,只有一个拷贝。

在定义静态成员函数(数据成员)时,不能指定关键字static。

普通的成员函数一般都隐含了一个this指针指向类对象本身,因为普通的成员函数(数据成员)属于具体的类对象。而静态成员函数(数据成员)不和任何对象联系,因此它不具有this指针。所以它不能访问类的非静态数据成员和成员函数。故,静态成员函数只能访问静态数据成员和静态函数成员。

非静态成员函数可以访问静态和非静态成员。

由于没有this指针的额外开销,因此静态成员函数和类的全局函数相比速度上会有少许的增长。

调用静态成员函数,可以用成员访问操作符‘.’和‘->’,<类名>::<静态成员函数名>(<参数列表>)

不能将静态成员函数定义为虚函数

静态函数成员可以在类内定义(区别于静态数据成员)。

为了防止父类的影响,可以在子类定义一个与父类相同的静态变量来屏蔽父类的影响。这里,静态成员为父类和子类共享,重复定义静态成员不会引起错误,因为编译器采用了name-mangling用以生成唯一的标志。

static