static主要有三個做用ios
(1)局部靜態變量c++
(2)外部靜態變量/函數安全
(3)靜態數據成員/成員函數多線程
前兩種C和C++都有,第三種僅C++特有。下面分別介紹。ide
1、局部靜態變量函數
在C/C++中,局部變量按存儲方式可分爲auto,static,register,register不多用到,下面主要說說auto和static的區別。this
一、存儲空間分配和生存週期不一樣。
spa
二、static局部靜態變量在所處模塊初次運行時初始化,且只操做一次。線程
三、對於局部靜態變量,若是不賦初值,編譯時會自動賦初值0或空字符,而auto局部變量的初始值是不肯定的。(對於C++的class對象例外,class的對象實例若是不初始化,則會自動調用默認構造函數)設計
static局部靜態變量具備「記憶性」和生存期的「全局性」
「記憶性」是在再次調入函數時,static局部靜態變量保持前一次退出時的值。
#include <iostream> using namespace std; void localVar() { static int a=0; //運行時初始化一次,下次調用時,不進行初始化 cout<< "a="<<a<<endl; a++; } void main(void) { localVar(); //輸出a=0; localVar(); //記憶了第一次退出時的值,輸出a=1 }
「全局性」可以延長變量的生存期, 改善」return a pointer / reference to a local object」的問題。例如:
const char * IpToStr(UINT32 IpAddr) { static char strBuff[16]; // static局部變量, 用於返回地址有效 const unsigned char *pChIP = (const unsigned char *)&IpAddr; sprintf(strBuff, "%u.%u.%u.%u", pChIP[0], pChIP[1], pChIP[2], pChIP[3]); return strBuff; }
注意:因爲static具備全局惟一性的特色,每次調用時,都指向同一塊內存,這就形成了一個很嚴重的問題——不可重入性。這在多線程程序設計和遞歸程序設計中要特別注意。下面針對上述示例分析多線程狀況下的不安全性。
假設如今有兩個線程A,B運行期間都須要調用IpToStr()函數, 將32位的IP地址轉換成點分10進制的字符串形式. 現A先得到執行機會, 執行IpToStr(), 傳入的參數是0x0B090A0A, 順序執行完應該返回的指針存儲區內容是:」10.10.9.11」, 現執行到⑥時, 失去執行權, 調度到B線程執行, B線程傳入的參數是0xA8A8A8C0, 執行至⑦, 靜態存儲區的內容是192.168.168.168. 當再調度到A執行時, 從⑥繼續執行, 因爲strBuff的全局惟一性, 內容已經被B線程沖掉, 此時返回的將是192.168.168.168字符串, 再也不是10.10.9.11字符串。
2、外部靜態變量/函數
在C中static有第二種含義:用來表示不能被其餘文件訪問的全局變量和函數,即在變量和函數前加static,是爲了限制其做用域僅限於本文件(因此又稱內部函數)。不管是否有static限制, 它的存儲區域都是在靜態存儲區, 生存期都是全局的. 此時的static只是起做用域限制做用, 限定做用域在本模塊(文件)內部。使用內部函數的好處是:不一樣的人編寫不一樣的函數時,不用擔憂本身定義的函數,是否會與其它文件中的函數同名。例如:
#fiel1.cpp
static int a=0;
int b;
extern void funA()
{}
static void funB()
{}
#file2.cpp
extern int a;//錯誤
extern int b;//正確
extern void funA()//正確
extern void funB()//錯誤
3、靜態數據成員/成員函數(C++特有)
c++重用了這個關鍵字,並賦予其第三個含義:表示靜態數據成員/成員函數屬於一個類,而不屬於某個對象,這是與普通成員函數的最大區別。 好比在對某一個類的對象進行計數時, 計數生成多少個類的實例, 就能夠用到靜態數據成員. 在這裏面, static既不是限定做用域的, 也不是擴展生存期的做用, 而是指示變量/函數在此類中的惟一性. 這也是」屬於一個類而不是屬於此類的任何特定對象的變量和函數」的含義. 由於它是對整個類來講是惟一的, 所以不可能屬於某一個實例對象的。
先舉一個靜態數據成員的例子
#include<iostream>
using namespace std;
class Myclass
{
private:
int a , b , c;
static int sum; //聲明靜態數據成員
public:
Myclass(int a , int b , int c);
void GetSum();
};
int Myclass::sum = 0; //定義並初始化靜態數據成員
Myclass::Myclass(int a , int b , int c)
{
this->a = a;
this->b = b;
this->c = c;
sum += a+b+c;
}
void Myclass::GetSum()
{
cout<<"sum="<<sum<<endl;
}
int main(void)
{
Myclass M(1 , 2 , 3);
M.GetSum(); //輸出結果6
Myclass N(4 , 5 , 6);
N.GetSum(); //輸出結果21
return 0;
}
靜態成員函數
與靜態數據成員同樣,也是類的內部實現,屬於類的內部實現,
普通的成員函數通常隱含了this指針,指向類對象自己,由於普通成員函數老是具體的屬於某個類的具體對象的。一般狀況下,this是缺省的。如函數fn()其實是this->fn()。可是與普通函數相比,靜態成員函數因爲不是與任何的對象相聯繫,所以它不具備this指針。從這個意義上講,它沒法訪問屬於類對象的非靜態數據成員,也沒法訪問非靜態成員函數,它只能調用其他的靜態成員函數。舉例:
#include<iostream>
using namespace std;
class Myclass
{
private:
int a , b , c;
static int sum; //聲明靜態數據成員
public:
Myclass(int a , int b , int c);
static void GetSum(); //聲明靜態成員函數
};
int Myclass::sum = 0; //定義並初始化靜態數據成員
Myclass::Myclass(int a , int b , int c)
{
this->a = a;
this->b = b;
this->c = c;
sum += a+b+c; //非靜態成員函數能夠訪問靜態數據成員
}
void Myclass::GetSum() //靜態成員函數的實現
{
//cout<<a<<endl; //錯誤代碼,a是非靜態數據成員
cout<<"sum="<<sum<<endl;
}
int main(void)
{
Myclass M(1 , 2 , 3);
M.GetSum();
Myclass N(4 , 5 , 6);
N.GetSum();
Myclass::GetSum();
return 0;
}
另外, 在設計類的多線程操做時, 因爲POSIX庫下的線程函數pthread_create()要求是全局的, 普通成員函數沒法直接作爲線程函數, 能夠考慮用Static成員函數作線程函數。