今日從網(wǎng)上看到一篇好文章,匯總匯總,又拼湊拼湊,便有了下文。
static關(guān)鍵字是C、C++中都存在的關(guān)鍵字, 它主要有三種使用方式, 其中前兩種只指在C語言中使用, 第三種在C++中使用(C,C++中具體細(xì)微操作不盡相同, 本文以C++為準(zhǔn)).
(1) 局部靜態(tài)變量 靜態(tài)局部變量有兩個用法,記憶功能和全局生存期.
(2) 外部靜態(tài)變量/函數(shù) 用于全局變量,主要作用是限制此全局變量被其他的文件調(diào)用
(3) 靜態(tài)數(shù)據(jù)成員/成員函數(shù) 表示這個成員是屬于這個類但是不屬于類中任意特定對象
下面就這三種使用方式及注意事項分別說明
一、局部靜態(tài)變量
在C/C++中, 局部變量按照存儲形式可分為三種auto, static, register
與auto類型(普通)局部變量相比, static局部變量有三點不同
1. 存儲空間分配不同
auto類型分配在棧上, 屬于動態(tài)存儲類別, 占動態(tài)存儲區(qū)空間, 函數(shù)調(diào)用結(jié)束后自動釋放, 而static分配在靜態(tài)存儲區(qū), 在程序整個運行期間都不釋放. 兩者之間的作用域相同, 但生存期不同.
2. static局部變量在所處模塊在初次運行時進(jìn)行初始化工作, 且只操作一次
3. 對于局部靜態(tài)變量, 如果不賦初值, 編譯期會自動賦初值0或空字符, 而auto類型的初值是不確定的.(對于C++中的class對象例外, class的對象實例如果不初始化, 則會自動調(diào)用默認(rèn)構(gòu)造函數(shù), 不管是否是static類型)
特點: static局部變量的”記憶性”與生存期的”全局性”
所謂“記憶性”是指在兩次函數(shù)調(diào)用時, 在第二次調(diào)用進(jìn)入時, 能保持第一次調(diào)用退出時的值.
示例程序一
#include <iostream>
using namespace std;
void staticLocalVar()
{
static int a = 0; // 運行期時初始化一次,下次再調(diào)用時,不進(jìn)行初始化工作
cout<<"a="<<a<<endl;
++a;
}
int main()
{
staticLocalVar(); // 第一次調(diào)用, 輸出a=0
staticLocalVar(); // 第二次調(diào)用, 記憶了第一次退出時的值, 輸出a=1
return 0;
}
應(yīng)用: 利用“記憶性”, 記錄函數(shù)調(diào)用的次數(shù)(示例程序一)
利用生存期的“全局性”,改善
“return a pointer / reference to a local object”的問題. Local object的問題在于退出函數(shù), 生存期即結(jié)束,利用static的作用, 延長變量的生存期.
示例程序二:
// IP address to string format
// Used in Ethernet Frame and IP Header analysis
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;
}
注意事項:
1. “記憶性”, 程序運行很重要的一點就是可重復(fù)性, 而static變量的“記憶性”破壞了這種可重復(fù)性, 造成不同時刻至運行的結(jié)果可能不同.
2. “生存期”全局性和唯一性. 普通的local變量的存儲空間分配在stack上, 因此每次調(diào)用函數(shù)時, 分配的空間都可能不一樣, 而static具有全局唯一性的特點, 每次調(diào)用時, 都指向同一塊內(nèi)存, 這就造成一個很重要的問題 ---- 不可重入性!!!
這樣在多線程程序設(shè)計或遞歸程序設(shè)計中, 要特別注意這個問題.
下面針對示例程序二, 分析在多線程情況下的不安全性.(為方便描述, 標(biāo)上行號)
①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;
⑦ }
假設(shè)現(xiàn)在有兩個線程A,B運行期間都需要調(diào)用IpToStr()函數(shù), 將32位的IP地址轉(zhuǎn)換成點分10進(jìn)制的字符串形式.
現(xiàn)A先獲得執(zhí)行機(jī)會, 執(zhí)行IpToStr(), 傳入的參數(shù)是0x0B090A0A, 順序執(zhí)行完應(yīng)該返回的指針存儲區(qū)內(nèi)容是: “10.10.9.11”, 現(xiàn)執(zhí)行到⑥時, 失去執(zhí)行權(quán), 調(diào)度到B線程執(zhí)行, B線程傳入的參數(shù)是0xA8A8A8C0,執(zhí)行至⑦, 靜態(tài)存儲區(qū)的內(nèi)容是192.168.168.168. 當(dāng)再調(diào)度到A執(zhí)行時, 從⑥繼續(xù)執(zhí)行, 由于strBuff的全局唯一性, 內(nèi)容已經(jīng)被B線程沖掉, 此時返回的將是192.168.168.168字符串, 不再是10.10.9.11字符串.
補(bǔ)充:靜態(tài)局部變量屬于靜態(tài)存儲方式,它具有以下特點:
(1)靜態(tài)局部變量在函數(shù)內(nèi)定義 它的生存期為整個源程序,但是其作用域仍與自動變量相同,只能在定義該變量的函數(shù)內(nèi)使用該變量。退出該函數(shù)后,盡管該變量還繼續(xù)存在,但不能使用它。
(2)允許對構(gòu)造類靜態(tài)局部量賦初值 例如數(shù)組,若未賦以初值,則由系統(tǒng)自動賦以0值。
(3)對基本類型的靜態(tài)局部變量若在說明時未賦以初值,則系統(tǒng)自動賦予0值。而對自動變量不賦初值,則其值是不定的。根據(jù)靜態(tài)局部變量的特點, 可以看出它是一種生存期為整個源程序的量。雖然離開定義它的函數(shù)后不能使用,但如再次調(diào)用定義它的函數(shù)時,它又可繼續(xù)使用, 而且保存了前次被調(diào)用后留下的值。因此,當(dāng)多次調(diào)用一個函數(shù)且要求在調(diào)用之間保留某些變量的值時,可考慮采用靜態(tài)局部變量。雖然用全局變量也可以達(dá)到上述目的,但全局變量有時會造成意外的副作用,因此仍以采用局部靜態(tài)變量為宜
二、外部靜態(tài)變量/函數(shù)
在C中static有了第二種含義:用來表示不能被其它文件訪問的全局變量和函數(shù)。 但為了限制全局變量/函數(shù)的作用域, 函數(shù)或變量前加static使得函數(shù)成為靜態(tài)函數(shù)。但此處“static”的含義不是指存儲方式,而是指對函數(shù)的作用域僅局限于本文件(所以又稱內(nèi)部函數(shù))。注意此時, 對于外部(全局)變量, 不論是否有static限制, 它的存儲區(qū)域都是在靜態(tài)存儲區(qū), 生存期都是全局的. 此時的static只是起作用域限制作用, 限定作用域在本模塊(文件)內(nèi)部.
使用內(nèi)部函數(shù)的好處是:不同的人編寫不同的函數(shù)時,不用擔(dān)心自己定義的函數(shù),是否會與其它文件中的函數(shù)同名。
示例程序三:
//file1.cpp
static int varA;
int varB;
extern void funA()
{
……
}
static void funB()
{
……
}
//file2.cpp
extern int varB; // 使用file1.cpp中定義的全局變量
extern int varA; // 錯誤! varA是static類型, 無法在其他文件中使用
extern vod funA(); // 使用file1.cpp中定義的函數(shù)
extern void funB(); // 錯誤! 無法使用file1.cpp文件中static函數(shù)
補(bǔ)充:全局變量(外部變量)的說明之前再冠以static 就構(gòu)成了靜態(tài)的全局變量。全局變量本身就是靜態(tài)存儲方式, 靜態(tài)全局變量當(dāng)然也是靜態(tài)存儲方式。 這兩者在存儲方式上并無不同。這兩者的區(qū)別雖在于非靜態(tài)全局變量的作用域是整個源程序,當(dāng)一個源程序由多個源文件組成時,非靜態(tài)的全局變量在各個源文件中都是有效的。 而靜態(tài)全局變量則限制了其作用域, 即只在定義該變量的源文件內(nèi)有效, 在同一源程序的其它源文件中不能使用它。由于靜態(tài)全局變量的作用域局限于一個源文件內(nèi),只能為該源文件內(nèi)的函數(shù)公用,因此可以避免在其它源文件中引起錯誤。從以上分析可以看出, 把局部變量改變?yōu)殪o態(tài)變量后是改變了它的存儲方式即改變了它的生存期。把全局變量改變?yōu)殪o態(tài)變量后是改變了它的作用域,限制了它的使用范圍。因此static 這個說明符在不同的地方所起的作用是不同的。
三、靜態(tài)數(shù)據(jù)成員/成員函數(shù)(C++特有)
C++重用了這個關(guān)鍵字,并賦予它與前面不同的第三種含義:表示屬于一個類而不是屬于此類的任何特定對象的變量和函數(shù). 這是與普通成員函數(shù)的最大區(qū)別, 也是其應(yīng)用所在, 比如在對某一個類的對象進(jìn)行計數(shù)時,計數(shù)生成多少個類的實例, 就可以用到靜態(tài)數(shù)據(jù)成員. 在這里面, static既不是限定作用域的, 也不是擴(kuò)展生存期的作用, 指示變量/函數(shù)在此類中的唯一性. 這也是“屬于一個類而不是屬于此類的任何特定對象的變量和函數(shù)”的含義. 因為它是對整個類來說是唯一的, 因此不可能屬于某一個實例對象的. (針對靜態(tài)數(shù)據(jù)成員而言, 成員函數(shù)不管是否是static,在內(nèi)存中只有一個副本.普通成員函數(shù)調(diào)用時, 需要傳入this指針,static成員函數(shù)調(diào)用時, 沒有this指針. )
請看示例程序四
class EnemyTarget {
public:
EnemyTarget() { ++numTargets; }
EnemyTarget(const EnemyTarget&) { ++numTargets; }
~EnemyTarget() { --numTargets; }
static size_t numberOfTargets() { return numTargets; }
bool destroy(); // returns success of attempt to destroy
// EnemyTarget object
private:
static size_t numTargets; // object counter
};
// class statics must be defined outside the class;
// initialization is to 0 by default
size_t EnemyTarget::numTargets;
在這個例子中, 靜態(tài)數(shù)據(jù)成員numTargets就是用來計數(shù)產(chǎn)生的對象個數(shù)的.
在《c++ 程序設(shè)計語言》中,是這樣運用的:
Static靜態(tài)成員,它是類的一部分,但卻不是該類的各個對象的一部分。一個static成員只有唯一的一份副本,但不像常規(guī)的非static成員那樣在每個對象里各有一份副本。 與此類似,一個需要訪問類成員,然而卻并不需要針對特定對象去調(diào)用的函數(shù),也被稱為一個static成員函數(shù)。其好處在于消除了由于依賴全局量而引起的問題
Class Date
{
Int d, m, y;
Static Date default_date;
Public:
Date(int dd=0, int mm=0, int yy=0);
//……
Static void set_default(int, int, int);
};
靜態(tài)成員可以像任何其他成員一樣引用,此外,對于靜態(tài)成員的引用不必提到任何對象,相反,在這里應(yīng)該成員的名字加上作為限定詞的類的名字。
Void f()
{
Date::set_default(4, 5, 1945);
}
靜態(tài)成員(包括函數(shù)和數(shù)據(jù)成員)都必須在某個地方另行定義。如
Date Date:::default_date(16, 12, 1770);
Void Date::set_default(int d, int m, int y)
{
Date::default_date = Date(d, m, y);
}
補(bǔ)充:內(nèi)部函數(shù)和外部函數(shù)
當(dāng)一個源程序由多個源文件組成時,C語言根據(jù)函數(shù)能否被其它源文件中的函數(shù)調(diào)用,將函數(shù)分為內(nèi)部函數(shù)和外部函數(shù)。
1 內(nèi)部函數(shù)(又稱靜態(tài)函數(shù))
如果在一個源文件中定義的函數(shù),只能被本文件中的函數(shù)調(diào)用,而不能被同一程序其它文件中的函數(shù)調(diào)用,這種函數(shù)稱為內(nèi)部函數(shù)。
定義一個內(nèi)部函數(shù),只需在函數(shù)類型前再加一個“static”關(guān)鍵字即可,如下所示:
static 函數(shù)類型 函數(shù)名(函數(shù)參數(shù)表)
{……}
關(guān)鍵字“static”,譯成中文就是“靜態(tài)的”,所以內(nèi)部函數(shù)又稱靜態(tài)函數(shù)。但此處“static”的含義不是指存儲方式,而是指對函數(shù)的作用域僅局限于本文件。
使用內(nèi)部函數(shù)的好處是:不同的人編寫不同的函數(shù)時,不用擔(dān)心自己定義的函數(shù),是否會與其它文件中的函數(shù)同名,因為同名也沒有關(guān)系。
2 外部函數(shù)
外部函數(shù)的定義:在定義函數(shù)時,如果沒有加關(guān)鍵字“static”,或冠以關(guān)鍵字“extern”,表示此函數(shù)是外部函數(shù):
[extern] 函數(shù)類型 函數(shù)名(函數(shù)參數(shù)表)
{……}
調(diào)用外部函數(shù)時,需要對其進(jìn)行說明:
[extern] 函數(shù)類型 函數(shù)名(參數(shù)類型表)[,函數(shù)名2(參數(shù)類型表2)……];
posted on 2008-10-11 10:40
Sandy 閱讀(381)
評論(1) 編輯 收藏 引用