1、概述
static 聲明的變量在C語言中有兩方面的特徵:
1)、變量會被放在程序的全局存儲區中,這樣可以在下一次調用的時候還可以保持原來的賦值。這一點是它與堆棧變量和堆變量的區別。
2、問題:Static的理解
關於static變量,請選擇下面所有說法正確的內容:
A、若全局變量僅在單個C文件中訪問,則可以將這個變量修改爲靜態全局變量,以降低模塊間的耦合度;
B、若全局變量僅由單個函數訪問,則可以將這個變量改爲該函數的靜態局部變量,以降低模塊間的耦合度;
C、設計和使用訪問動態全局變量、靜態全局變量、靜態局部變量的函數時,需要考慮重入問題;
D、靜態全局變量過大,可那會導致堆棧溢出。
答案與分析:
對於A,B:根據本篇概述部分的說明b),我們知道,A,B都是正確的。
對於C:根據本篇概述部分的說明a),我們知道,C是正確的(所謂的函數重入問題,下面會詳細闡述)。
對於D:靜態變量放在程序的全局數據區,而不是在堆棧中分配,所以不可能導致堆棧溢出,D是錯誤的。
因此,答案是A、B、C。
3、問題:不可重入函數
曾經設計過如下一個函數,在代碼檢視的時候被提醒有bug,因爲這個函數是不可重入的,爲什麼?
unsigned int sum_int( unsigned int base )
{ unsigned int index; static unsigned int sum = 0; // 注意,是static類型的。 for (index = 1; index <= base; index++) { sum += index; } return sum; } |
答案與分析:
所謂的函數是可重入的(也可以說是可預測的),即:只要輸入數據相同就應產生相同的輸出。
這個函數之所以是不可預測的,就是因爲函數中使用了static變量,因爲static變量的特徵,這樣的函數被稱爲:帶“內部存儲器”功能的的函數。因此如果我們需要一個可重入的函數,那麼,我們一定要避免函數中使用static變量,這種函數中的static變量,使用原則是,能不用盡量不用。
將上面的函數修改爲可重入的函數很簡單,只要將聲明sum變量中的static關鍵字去掉,變量sum即變爲一個auto 類型的變量,函數即變爲一個可重入的函數。
當然,有些時候,在函數中是必須要使用static變量的,比如當某函數的返回值爲指針類型時,則必須是static的局部變量的地址作爲返回值,若爲auto類型,則返回爲錯指針。
C++ 類的靜態成員(static) | ||
靜態成員的提出是爲了解決數據共享的問題。實現共享有許多方法,如:設置全局性的變量或對象是一種方法。但是,全局變量或對象是有侷限性的。這一章裏,我們主要講述類的靜態成員來實現數據的共享。 靜態數據成員 在類中,靜態成員可以實現多個對象之間的數據共享,並且使用靜態數據成員還不會破壞隱藏的原則,即保證了安全性。因此,靜態成員是類的所有對象中共享的成員,而不是某個對象的成員。 使用靜態數據成員可以節省內存,因爲它是所有對象所公有的,因此,對多個對象來說,靜態數據成員只存儲一處,供所有對象共用。靜態數據成員的值對每個對象都是一樣,但它的值是可以更新的。只要對靜態數據成員的值更新一次,保證所有對象存取更新後的相同的值,這樣可以提高時間效率。 靜態數據成員的使用方法和注意事項如下: 1、靜態數據成員在定義或說明時前面加關鍵字static。 2、靜態成員初始化與一般數據成員初始化不同。靜態數據成員初始化的格式如下: <數據類型><類名>::<靜態數據成員名>=<值> 這表明: (1) 初始化在類體外進行,而前面不加static,以免與一般靜態變量或對象相混淆。 (2) 初始化時不加該成員的訪問權限控制符private,public等。 (3) 初始化時使用作用域運算符來標明它所屬類,因此,靜態數據成員是類的成員,而不是對象的成員。 3、靜態數據成員是靜態存儲的,它是靜態生存期,必須對它進行初始化。 4、引用靜態數據成員時,採用如下格式: <類名>::<靜態成員名> 如果靜態數據成員的訪問權限允許的話(即public的成員),可在程序中,按上述格式來引用靜態數據成員。 下面舉一例子,說明靜態數據成員的應用:
從輸出結果可以看到Sum的值對M對象和對N對象都是相等的。這是因爲在初始化M對象時,將M對象的三個int型數據成員的值求和後賦給了Sum,於是Sum保存了該值。在初始化N對象時,對將N對象的三個int型數據成員的值求和後又加到Sum已有的值上,於是Sum將保存另後的值。所以,不論是通過對象M還是通過對象N來引用的值都是一樣的,即爲54。
靜態成員函數
讀者可以自行分析其結果。從中可看出,調用靜態成員函數使用如下格式: <類名>::<靜態成員函數名>(<參數表>); |
在多線程條件下,函數應當是線程安全的,進一步,更強的條件是可重入的。
一個可重入函數保證了在多線程條件下,函數的狀態不會出現錯誤。
eg. in c
static int tmp;
void swap1(int* x, int* y) {
tmp=*x;
*x=*y;
*y=tmp;
}
void swap2(int* x, int* y) {
int tmp;
tmp=*x;
*x=*y;
*y=tmp;
}
swap1是不可重入的,swap是可重入的。因爲在多線程條件下,操作系統會在swap1還沒有執行完的情況下,切換到另一個線程中,那個線程可能再次調用swap1,這樣狀態就錯了。
一個函數如果滿足以及下條件之一,那麼它是不可重入的:
- 函數中使用了靜態變量,無論是全局靜態變量還是局部靜態變量。
- 函數返回靜態變量。
- 函數中調用了不可重入函數。
- 函數是singleton中的成員函數而且使用了不使用線程獨立存儲的成員變量
總的來說,如果一個函數在重入條件下使用了未受保護的共享的資源,那麼它是不可重入的
可重入(reentrant)函數可以由多於一個任務併發使用,而不必擔心數據錯誤。相反, 不可重入(non-reentrant)函數不能由超過一個任務所共享,除非能確保函數的互斥(或者使用信號量,或者在代碼的關鍵部分禁用中斷)。可重入函數可以在任意時刻被中斷,稍後再繼續運行,不會丟失數據。可重入函數要麼使用本地變量,要麼在使用全局變量時保護自己的數據。
可重入函數:
- 不爲連續的調用持有靜態數據。
- 不返回指向靜態數據的指針;所有數據都由函數的調用者提供。
- 使用本地數據,或者通過製作全局數據的本地拷貝來保護全局數據。
- 絕不調用任何不可重入函數。