c 與c++ static 函數(shù)的區(qū)別
static 關(guān)鍵字是 C, C++ 中都存在的關(guān)鍵字 , 它主要有三種使用方式 , 其中前兩種只指在 C 語言中使用 , 第三種在 C++ 中使用 (C,C++ 中具體細(xì)微操作不盡相同 , 本文以 C++ 為準(zhǔn) ).
(1) 局部靜態(tài)變量
(2) 外部靜態(tài)變量 / 函數(shù)
(3) 靜態(tài)數(shù)據(jù)成員 / 成員函數(shù)
下面就這三種使用方式及注意事項(xiàng)分別說明
一、局部靜態(tài)變量
在 C/C++ 中 , 局部變量按照存儲形式可分為三種 auto, static, register
static 關(guān)鍵字是 C, C++ 中都存在的關(guān)鍵字 , 它主要有三種使用方式 , 其中前兩種只指在 C 語言中使用 , 第三種在 C++ 中使用 (C,C++ 中具體細(xì)微操作不盡相同 , 本文以 C++ 為準(zhǔn) ).
(1) 局部靜態(tài)變量
(2) 外部靜態(tài)變量 / 函數(shù)
(3) 靜態(tài)數(shù)據(jù)成員 / 成員函數(shù)
下面就這三種使用方式及注意事項(xiàng)分別說明
一、局部靜態(tài)變量
在 C/C++ 中 , 局部變量按照存儲形式可分為三種 auto, static, register
(<C 語言程序設(shè)計 ( 第二版 )> 譚浩強(qiáng) , 第 174-175 頁 )
與 auto 類型 ( 普通 ) 局部變量相比 , static 局部變量有三點(diǎn)不同
1. 存儲空間分配不同
auto 類型分配在棧上 , 屬于動態(tài)存儲類別 , 占動態(tài)存儲區(qū)空間 , 函數(shù)調(diào)用結(jié)束后自動釋放 , 而 static 分配在靜態(tài)存儲區(qū) , 在程序整個運(yùn)行期間都不釋放 . 兩者之間的作用域相同 , 但生存期不同 .
2. static 局部變量在所處模塊在初次運(yùn)行時進(jìn)行初始化工作 , 且只操作一次
3. 對于局部靜態(tài)變量 , 如果不賦初值 , 編譯期會自動賦初值 0 或空字符 , 而 auto 類型的初值是不確定的 . ( 對于 C++ 中的 class 對象例外 , class 的對象實(shí)例如果不初始化 , 則會自動調(diào)用默認(rèn)構(gòu)造函數(shù) , 不管是否是 static 類型 )
特點(diǎn) : static 局部變量的”記憶性”與生存期的”全局性”
所謂”記憶性”是指在兩次函數(shù)調(diào)用時 , 在第二次調(diào)用進(jìn)入時 , 能保持第一次調(diào)用退出時的值 .
示例程序一
#include <iostream>
using namespace std;
void staticLocalVar()
{
static int a = 0; // 運(yùn)行期時初始化一次 , 下次再調(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;
}
注意事項(xiàng) :
1. “記憶性” , 程序運(yùn)行很重要的一點(diǎn)就是可重復(fù)性 , 而 static 變量的”記憶性”破壞了這種可重復(fù)性 , 造成不同時刻至運(yùn)行的結(jié)果可能不同 .
2. “生存期”全局性和唯一性 . 普通的 local 變量的存儲空間分配在 stack 上 , 因此每次調(diào)用函數(shù)時 , 分配的空間都可能不一樣 , 而 static 具有全局唯一性的特點(diǎn) , 每次調(diào)用時 , 都指向同一塊內(nèi)存 , 這就造成一個很重要的問題 ---- 不可重入性 !!!
這樣在多線程程序設(shè)計或遞歸程序設(shè)計中 , 要特別注意這個問題 .
( 不可重入性的例子可以參見 <effective C++ (2nd)>( 影印版 ) 第 103-105 頁 )
下面針對示例程序二 , 分析在多線程情況下的不安全性 .( 為方便描述 , 標(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 運(yùn)行期間都需要調(diào)用 IpToStr() 函數(shù) , 將 32 位的 IP 地址轉(zhuǎn)換成點(diǎ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 字符串 .
二、外部靜態(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ù)
三、靜態(tài)數(shù)據(jù)成員/成員函數(shù) (C++ 特有 )
C+ + 重用了這個關(guān)鍵字,并賦予它與前面不同的第三種含義:表示屬于一個類而不是屬于此類的任何特定對象的變量和函數(shù) . 這是與普通成員函數(shù)的最大區(qū)別 , 也是其應(yīng)用所在 , 比如在對某一個類的對象進(jìn)行計數(shù)時 , 計數(shù)生成多少個類的實(shí)例 , 就可以用到靜態(tài)數(shù)據(jù)成員 . 在這里面 , static 既不是限定作用域的 , 也不是擴(kuò)展生存期的作用 , 而是指示變量 / 函數(shù)在此類中的唯一性 . 這也是”屬于一個類而不是屬于此類的任何特定對象的變量和函數(shù)”的含義 . 因?yàn)樗菍φ麄€類來說是唯一的 , 因此不可能屬于某一個實(shí)例對象的 . ( 針對靜態(tài)數(shù)據(jù)成員而言 , 成員函數(shù)不管是否是 static, 在內(nèi)存中只有一個副本 , 普通成員函數(shù)調(diào)用時 , 需要傳入 this 指針 , static 成員函數(shù)調(diào)用時 , 沒有 this 指針 . )
請看示例程序四 (<effective c++ (2nd)>( 影印版 ) 第 59 頁 )
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ù)的 .
另外 , 在設(shè)計類的多線程操作時 , 由于 POSIX 庫下的線程函數(shù) pthread_create() 要求是全局的 , 普通成員函數(shù)無法直接做為線程函數(shù) , 可以考慮用 Static 成員函數(shù)做線程函數(shù) .
ZZ自:http://www.shnenglu.com/mydriverc/articles/31665.html
與 auto 類型 ( 普通 ) 局部變量相比 , static 局部變量有三點(diǎn)不同
1. 存儲空間分配不同
auto 類型分配在棧上 , 屬于動態(tài)存儲類別 , 占動態(tài)存儲區(qū)空間 , 函數(shù)調(diào)用結(jié)束后自動釋放 , 而 static 分配在靜態(tài)存儲區(qū) , 在程序整個運(yùn)行期間都不釋放 . 兩者之間的作用域相同 , 但生存期不同 .
2. static 局部變量在所處模塊在初次運(yùn)行時進(jìn)行初始化工作 , 且只操作一次
3. 對于局部靜態(tài)變量 , 如果不賦初值 , 編譯期會自動賦初值 0 或空字符 , 而 auto 類型的初值是不確定的 . ( 對于 C++ 中的 class 對象例外 , class 的對象實(shí)例如果不初始化 , 則會自動調(diào)用默認(rèn)構(gòu)造函數(shù) , 不管是否是 static 類型 )
特點(diǎn) : static 局部變量的”記憶性”與生存期的”全局性”
所謂”記憶性”是指在兩次函數(shù)調(diào)用時 , 在第二次調(diào)用進(jìn)入時 , 能保持第一次調(diào)用退出時的值 .
示例程序一
#include <iostream>
using namespace std;
void staticLocalVar()
{
static int a = 0; // 運(yùn)行期時初始化一次 , 下次再調(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;
}
注意事項(xiàng) :
1. “記憶性” , 程序運(yùn)行很重要的一點(diǎn)就是可重復(fù)性 , 而 static 變量的”記憶性”破壞了這種可重復(fù)性 , 造成不同時刻至運(yùn)行的結(jié)果可能不同 .
2. “生存期”全局性和唯一性 . 普通的 local 變量的存儲空間分配在 stack 上 , 因此每次調(diào)用函數(shù)時 , 分配的空間都可能不一樣 , 而 static 具有全局唯一性的特點(diǎn) , 每次調(diào)用時 , 都指向同一塊內(nèi)存 , 這就造成一個很重要的問題 ---- 不可重入性 !!!
這樣在多線程程序設(shè)計或遞歸程序設(shè)計中 , 要特別注意這個問題 .
( 不可重入性的例子可以參見 <effective C++ (2nd)>( 影印版 ) 第 103-105 頁 )
下面針對示例程序二 , 分析在多線程情況下的不安全性 .( 為方便描述 , 標(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 運(yùn)行期間都需要調(diào)用 IpToStr() 函數(shù) , 將 32 位的 IP 地址轉(zhuǎn)換成點(diǎ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 字符串 .
二、外部靜態(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ù)
三、靜態(tài)數(shù)據(jù)成員/成員函數(shù) (C++ 特有 )
C+ + 重用了這個關(guān)鍵字,并賦予它與前面不同的第三種含義:表示屬于一個類而不是屬于此類的任何特定對象的變量和函數(shù) . 這是與普通成員函數(shù)的最大區(qū)別 , 也是其應(yīng)用所在 , 比如在對某一個類的對象進(jìn)行計數(shù)時 , 計數(shù)生成多少個類的實(shí)例 , 就可以用到靜態(tài)數(shù)據(jù)成員 . 在這里面 , static 既不是限定作用域的 , 也不是擴(kuò)展生存期的作用 , 而是指示變量 / 函數(shù)在此類中的唯一性 . 這也是”屬于一個類而不是屬于此類的任何特定對象的變量和函數(shù)”的含義 . 因?yàn)樗菍φ麄€類來說是唯一的 , 因此不可能屬于某一個實(shí)例對象的 . ( 針對靜態(tài)數(shù)據(jù)成員而言 , 成員函數(shù)不管是否是 static, 在內(nèi)存中只有一個副本 , 普通成員函數(shù)調(diào)用時 , 需要傳入 this 指針 , static 成員函數(shù)調(diào)用時 , 沒有 this 指針 . )
請看示例程序四 (<effective c++ (2nd)>( 影印版 ) 第 59 頁 )
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ù)的 .
另外 , 在設(shè)計類的多線程操作時 , 由于 POSIX 庫下的線程函數(shù) pthread_create() 要求是全局的 , 普通成員函數(shù)無法直接做為線程函數(shù) , 可以考慮用 Static 成員函數(shù)做線程函數(shù) .
ZZ自:http://www.shnenglu.com/mydriverc/articles/31665.html