++a;
}
intmain()
{
staticLocalVar();//第一次调用,输出a=0
staticLocalVar();//第二次调用,记忆了第一次退出时的值,输出a=1
return0;
}
应用:
利用”记忆性”,
记录函数调用的次数(示例程序一)
利用生存期的”全局性”
改善”returnapointer/referencetoalocalobject”的问题.Localobject的问题在于退出函数,生存期即结束,.利用static的作用,延长变量的生存期.
示例程序二:
//IPaddresstostringformat
//UsedinEthernetFrameandIPHeaderanalysis
constchar*IpToStr(UINT32IpAddr)
{
staticcharstrBuff[16];//static局部变量,用于返回地址有效
constunsignedchar*pChIP=(constunsignedchar*)&IpAddr;
sprintf(strBuff,"%u.%u.%u.%u",pChIP[0],pChIP[1],pChIP[2],pChIP[3]);
returnstrBuff;
}
注意事项:
1.“记忆性”,程序运行很重要的一点就是可重复性,而static变量的”记忆性”破坏了这种可重复性,造成不同时刻至运行的结果可能不同.
2.“生存期”全局性和唯一性.普通的local变量的存储空间分配在stack上,因此每次调用函数时,分配的空间都可能不一样,而static具有全局唯一性的特点,每次调用时,都指向同一块内存,这就造成一个很重要的问题----不可重入性!
!
!
这样在多线程程序设计或递归程序设计中,要特别注意这个问题.
(不可重入性的例子可以参见(影印版)第103-105页)下面针对示例程序二,分析在多线程情况下的不安全性.(为方便描述,标上行号)
①constchar*IpToStr(UINT32IpAddr)
②
{
③staticcharstrBuff[16];//static局部变量,
用于返回地址有效
④constunsignedchar*pChIP=(constunsignedchar*)&IpAddr;
⑤
sprintf(strBuff,"%u.%u.%u.%u",pChIP[0],pChIP[1],pChIP[2],pChIP[3]);
⑥returnstrBuff;
⑦
}
假设现在有两个线程A,B运行期间都需要调用IpToStr()函数,将32位的IP地址转换成点分10进制的字符串形式.现A先获得执行机会,执行IpToStr(),传入的参数是0x0B090A0A,
顺序执行完应该返回的指针存储区内容是:
”10.10.9.11”,
现执行到⑥时,失去执行权,调度到B线程执行,B线程传入的参数是0xA8A8A8C0,
执行至⑦,
⑥
二、外部静态变量/函数
在C中static有了第二种含义:
用来表示不能被其它文件访问的全局变量和函数。
但为了限制全局变量/函数的作用域,函数或变量前加static使得函数成为静态函数。
但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。
注意此时,对于外部(全局)变量,不论是否有static限制,它的存储区域都是在静态存储区,生存期都是全局的.此时的static只是起作用域限制作用,限定作用域在本模块(文件)内部.
使用内部函数的好处是:
不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。
示例程序三:
//file1.cpp
staticintvarA;
intvarB;
externvoidfunA()
{
……
}
staticvoidfunB()
{
……
}
//file2.cpp
externintvarB;//使用file1.cpp中定义的全局变量
externintvarA;//错误!
varA是static类型,无法在其他文件中使用
externvodfunA();//使用file1.cpp中定义的函数
externvoidfunB();//错误!
无法使用file1.cpp文件中static函数
2.extern用法
外部函数:
(1)在定义函数时,如果在函数首部的最左端冠以关键字extern,则表示此函数是外部函数,可供其它文件调用。
如函数首部可以写为
externintfun(inta,intb)
这样,函数fun就可以为其它文件调用。
C语言规定,如果在定义函数时省略extern,则隐含为外部函数。
(2)在需要调用此函数的文件中,用extern声明所用的函数是外部函数。