网上的几个介绍 引用在这里stati c关键字是C, C++中都存在的关键字,它主要有三种使用方式,其中前两种在C/C++语言中使用,第三种只在C++中使用(C,C++中具体细微操作不尽相同,本文以C++为准) .
(1)局部静态变量
(2)外部静态变量/函数
(3)静态数据成员/成员函数
下面就这三种使用方式及注意事项分别说明
一、局部静态变量
在C/C++中,局部变量按照存储形式可分为三种auto, stati c, register
(<C语言程序设计(第二版)>谭浩强, 第174-175页)
与auto类型(普通)局部变量相 比, stati c局部变量有三点不同
1.存储空间分配不同auto类型分配在栈上,属于动态存储类别, 占动态存储区空间, 函数调用结束后自动释放,而stat ic分配在静态存储区 ,在程序整个运行期间都不释放.两者之间的作用域相同 ,但生存期不同.
2. stati c局部变量在所处模块在初次运行时进行初始化工作,且只操作一次
3.对于局部静态变量,如果不赋初值,编译期会自动赋初值0或空字符,而auto类型的初值是不确定的. (对于C++中的cla ss对象例外, class的对象实例如果不初始化,则会自动调用默认构造函数,不管是否是stati c类型)
特点: stati c局部变量的”记忆性”与生存期的 ”全局性”
所谓”记忆性”是指在两次函数调用时,在第二次调用进入时, 能保持第一次调用退出 时的值.
示例程序一
#include <iostream>using namespace std;void stati cLoca lVar()
{stati c int a = 0; //运行期时初始化一次,下次再调用 时,不进行初始化工作cout<<"a="<<a<<endl ;
++a;
}int main()
{s tat i cLoca lVar() ; //第一次调用,输出a=0s tat i cLoca lVar() ; //第二次调用,记忆了第一次退出时的值,输出a=1return 0;
}
应用:
利用”记忆性” , 记录函数调用的次数(示例程序一)
利用生存期 的”全局性” , 改善” return a pointer / reference to a local object”的问题. Local objec t的问题在于退出函数,生存期即结束, .利用static的作用,延长变量的生存期.
示例程序二:
// IP addre ss to strin g format
// Used in Ethernet Frame and IP Header analy sisconst char * IpToS tr(UINT32 IpAddr)
{stati c char strBu ff[16] ; // stati c局部变量,用于返回地址有效const unsig ned char *pChIP = (const unsigned char *)&IpAdd r;sprin tf(strBu ff, "%u.%u.%u.%u", pChIP [0], pChIP [1], pChIP [2], pChIP [3]) ;return strBuff;
}
注意事项:
1. “记忆性”,程序运行很重要的一点就是可重复性,而stat ic变量的”记忆性”破坏了这种可重复性,造成不同时刻至运行的结果可能不 同.
2. “生存期”全局性和唯一性.普通的local变量的存储空间分配在stack上, 因此每次调用函数时, 分配的空间都可能不一样,而stat ic具有全局唯一性的特点,每次调用时,都指向同一块内存,这就造成一个很重要的 问题----不可重入性! ! !
这样在多线程程序设计或递归程序设计中, 要特别注意这个问题.
(不可重入性的例子可以参见<effec tive C++ (2nd)>(影印版)第103-105页)
下面针对示例程序二,分析在多线程情况下的不安全性. (为方便描述,标上行号)
①const char * IpToS tr(UINT32 IpAddr)
②{
③ stati c char strBu ff[16] ; // stati c局部变量,用于返回地址有效
④ const unsig ned char *pChIP = (const unsigned char *)&IpAdd r;
⑤ sprin tf(strBu ff, "%u.%u.%u.%u", pChIP [0], pChIP [1], pChIP[2], pChIP [3]) ;
⑥ return strBuff;
⑦}
假设现在有两个线程A,B运行期间都需要调用 IpToS tr()函数,将32位的IP地址转换成点分1 0进制的字符串形式. 现A先获得执行机会,执行IpT oStr(),传入的参数是0x0B 090A0 A,顺序执行完应该返回的指针存储区 内容是: ” 10. 10.9. 11” ,现执行到⑥时, 失去执行权,调度到B线程执行, B线程传入的参数是0 xA8A8 A8C0, 执行至⑦,静态存储区 的内容是1 92. 168. 168. 168.当再调度到A执行时, 从⑥继续执行, 由于strBuff的全局唯一性,内容已经被B线程冲掉,此时返回的将是192 . 168. 168. 168字符串,不再是10. 10.9. 11字符串 .
二、外部静态变量函数
在C中static有了第二种含义用来表示不 能被其它文件访问的全局变量和函数。,但为了限制全局变量/函数的作用域,函数或变量前加static使得函数成为静态函数。但此处“static”的含义不是指存储方式而是指对函数的作用域仅局限于本文件(所以又称内部函数) 。注意此时,对于外部(全局)变量, 不论是否有stati c限制, 它的存储区域都是在静态存储区,生存期都是全局的. 此时的st atic只是起作用域限制作用, 限定作用域在本模块(文件)内部.
使用内部函数的好处是不同的人编写不同的函数时不用担心自己定义的函数是否会与其它文件中的 函数同名。
示例程序三:
//f i l e 1 .cppstati c int varA;int varB;
extern void funA()
{
„„
}stati c void funB()
{
„„
}
//f i l e2.cppextern int varB; //使用fil e1.cpp中定义的全局变量exter n int varA; //错误! varA是stati c类型,无法在其他文件中使用extern vod funA() ; //使用fil e1.cpp中定义的函数exter n void funB() ; //错误!无法使用f ile1. cpp文件中stat ic函数
三、静态数据成员成员函数(C++特有)
C++重用了这个关键字并赋予它与前面不同的第三种含义表示属于一个类而不是属于此类的任何特定对象的变量和函数.这是与普通成员函数的最大区别,也是其应用所在, 比如在对某一个类的对象进行计数时,计数生成多少个类的实例,就可以用到静态数据成员.在这里面,stati c既不是限定作用域的,也不是扩展生存期的作用,而是指示变量/函数在此类中的唯一性.这也是”属于一个类而不是属于此类的任何特定对象的变量和函数”的含义. 因为它是对整个类来说是唯一的, 因此不可能属于某一个实例对象的. (针对静态数据成员而言,成员函数不
管是否是s tatic,在内存中只有一个副本,普通成员函数调用时,需要传入t his指针, static成员函数调用时, 没有thi s指针. )
请看示例程序四(<effec tive c++ (2nd)>(影印版)第59页)class Enemy Targe t {publi c:
EnemyTarge t() { ++numTa rgets ; }
Enemy Targe t(const Enemy Targe t&) { ++numTa rgets ; }
~Enemy Targe t() { --numTa rgets ; }stati c size_t numbe rOfTargets () { return numTa rgets ; }bool destroy() ; // returns succe ss of attempt to destroy EnemyTarge t objectpriva te:stati c size_t numTargets ; // object count er
} ;
// class stati cs must be defined outside the class ;
// initialization is to 0 by defaultsize_t Enemy Targe t: :numTa rgets ;
在这个例子中,静态数据成员numT arget s就是用来计数产生的对象个数的 .
另外, 在设计类的多线程操作时, 由于POS IX库下的线程函数p threa d_cre ate()要求是全局的,普通成员函数无法直接做为线程函数,可以考虑用Stati c成员函数做线程函数.
【我解C语言面试题系列 】001 stati c有什么用途
【题目】 stati c有什么用 途
在网上流传很广的一个答案是
1、限制变量的作用域
2、设置变量的存储域
我觉得这样答题是不妥当的有点文不对题的感觉。
下面是我给出的答案stati c类型声明符在C语言里面主要有三个用途
<!--[if !suppo rtLis ts]-->1、 <!--[endif]-->声明静态局部变量。
<!--[if !suppo rtLis ts]-->2、 <!--[endif]-->声明静态外部全局变量。
<!--[if !suppo rtLis ts]-->3、 <!--[endif]-->声明静态外部函数。
下面是我整理的有关上面三个用法的解释说明。另外网友x iaoca i0001的
《stati c用法小结》一文有更详细的解释请参考。
[url]http://blog. csdn.net/xiaoc ai000 1/archi ve/2006/04/14/66292 1.aspx[/url]静态局部变量与auto对比
<!--[if !suppo rtLis ts]-->1、 <!--[endif]-->存储空间分配、作用域和生存期stati c分配在静态存储区作用域仅仅限于声明该变量的函数内部。在程序整个运行期间都不释放生存期贯穿于程序运行的整个过程。auto类型分配在栈上属于动态存储类别 占动态存储区空间作用域仅仅限于声明该变量的函数内部。函数调用结束后自动释放生存期不过是在声明该变量的函数内部。
2、赋初值时的处理方式stati c静态局部变量在编译时赋初值 即只赋初值一次auto自动变量赋初值是在函数调用时进行每调用一次函数重新给一次初值相当于执行一次赋值语句。
3、未赋初值时的处理方式
如果在定义局部变量时不赋初值的话
stati c静态局部变量编译时自动赋初值0 对数值型变量或空字符对字符变量 。auto自动变量如果不赋初值则它的值是一个不确定的值。
静态外部全局变量
在C语言中stati c还用来声明静态外部全局变量那么这个全局变量的作用域就被限制在本文件内部。
外部变量即全局变量是在函数的外部定义的它的作用域为从变量定义处开始到本程序文件的末尾。如果外部变量不在文件的开头定义其有效的作用范围只限于定义处到文件终了。如果在定义点之前的函数想引用该外部变量则应该在引用之前用关键字ext ern对该变量作“外部变量声明” 。表示该变量是一个已经定义的外部变量。有了此声明就可以从“声明”处起合法地使用该外部变量。
而如果我们声明的全局变量不想被其他文件访问和使用又该怎么办
那就是在声明的时候前面加上关键字stat ic。
静态外部函数
在C语言中我们的函数默认都是全局的也就是说你可以调用其他文件中的函数。在使用的时候我们象前面一样在头文件中加上e xtern就可以了。但是有时候我们写的函数并不想让别的文件访问和调用那么我们在声明函数的时候前面加上stat ic就可以了。
使用内部函数的好处有二
1、可以让某些内部函数不为人所能使用而仅仅让调用者使用他能使用的东西有利于保护代码。
2、不同的人编写不同的函数时不用担心自己定义的函数是否会与其它文件中的函数同名
IonSwitch是一家2016年成立的国外VPS主机商,部落上一次分享的信息还停留在2019年,主机商提供基于KVM架构的VPS产品,数据中心之前在美国西雅图,目前是美国爱达荷州科德阿伦(美国西北部,西接华盛顿州和俄勒冈州),为新建的自营数据中心。商家针对新数据中心运行及4号独立日提供了一个5折优惠码,优惠后最低1GB内存套餐每月仅1.75美元起。下面列出部分套餐配置信息。CPU:1core内存...
PIGYun发布了九月份及中秋节特惠活动,提供8折优惠码,本月商家主推中国香港和韩国机房,优惠后最低韩国每月14元/中国香港每月19元起。这是一家成立于2019年的国人商家,提供中国香港、韩国和美国等地区机房VPS主机,基于KVM架构,采用SSD硬盘,CN2+BGP线路(美国为CUVIP-AS9929、GIA等)。下面列出两款主机配置信息。机房:中国香港CPU:1core内存:1GB硬盘:10GB...
木木云怎么样?木木云品牌成立于18年,此为贵州木木云科技有限公司旗下新运营高端的服务器的平台,目前已上线美国中部大盘鸡,母鸡采用E5-267X系列,硬盘全部组成阵列。目前,木木云美国vps进行了优惠促销,1核1G/500M带宽/1T硬盘/4T流量,仅35元/月。点击进入:木木云官方网站地址木木云优惠码:提供了一个您专用的优惠码: yuntue目前我们有如下产品套餐:DV型 1H 1G 500M带宽...