1. 程式人生 > >C Primer Plus (第五版)中文版——第 12 章 儲存類、連結和記憶體管理

C Primer Plus (第五版)中文版——第 12 章 儲存類、連結和記憶體管理

12.1  儲存類

12.1.1  作用域

定義:作用域描述了程式中可以訪問一個識別符號的一個或多個區域。

分類:

  • 程式碼塊作用域:在程式碼塊中定義的變數具有程式碼塊作用域,從定義處到包含該定義的程式碼塊的末尾,該變數可見。
  • 函式原型作用域:在函式原型中定義的變數具有函式原型作用域,從定義處到函式原型的末尾,該變數可見。
  • 檔案作用域:在所有函式之外定義的變數具有檔案作用域,從定義處到包含該定義的檔案的末尾,該變數可見。

說明:

  • 程式碼塊:包含在開始花括號和對應的結束花括號之內的一段程式碼。C99擴充套件到由迴圈語句或 if 語句所控制的程式碼。
  • 檔案作用域變數也被稱為全域性變數。

12.1.2  連結

定義:連結描述了程式的某個單元可被連結到其他哪些地方。

分類:

  • 外部連結:具有檔案作用域的變數可能有外部連結,該變數可以在一個多檔案程式的任何地方使用。
  • 內部連結:具有檔案作用域的變數可能有內部連結,該變數可以在一個檔案的任何地方使用。
  • 空連結:具有程式碼塊作用域或函式原型作用域的變數有空連結,該變數由其所在的程式碼塊或函式原型所私有。

說明:

  • 變數的作用域以及它的連結一起表明程式的哪些部分可以通過變數名來使用該變數。
  • 區分一個檔案作用域變數是具有內部連結還是外部連結:外部定義中是否使用了型別說明符 static:
int giants = 5;                //檔案作用域,外部連結
static int dodgers = 3;        //檔案作用域,內部連結
int main()
{
        ...
}
        ...

12.1.3  儲存時期

定義:變數在記憶體中保留的時間。

分類:

  • 靜態儲存時期:具有檔案作用域的變數具有靜態儲存時期,該變數在程式執行期間將一直存在。
  • 自動儲存時期:具有程式碼塊作用域的變數一般情況下具有自動儲存時期。當程式進入該變數的程式碼塊時,將為該變數分配記憶體;當程式退出程式碼塊時,分配的記憶體將被釋放。

C 使用作用域、連結和儲存時期來定義5中不同的儲存類:

儲存類 儲存時期 作用域 連結 宣告方式
自動 自動 程式碼塊 程式碼塊內
暫存器 自動 程式碼塊 程式碼塊內,使用關鍵字 register
具有外部連結的靜態 靜態 檔案 外部 所有函式之外
具有內部連結的靜態 靜態 檔案 內部 所有函式之外,使用關鍵字 static
具有空連結的靜態 靜態 程式碼塊 程式碼塊內,使用關鍵字 static

12.1.4  自動變數

int main(void)
{
        auto int plox;        //自動變數
  • 預設情況下,在程式碼塊或函式的頭部定義的任何變數都屬於自動儲存類,也可顯式地使用關鍵字 auto。
  • 程式碼塊作用域和空連結表明:只有在變數定義所在的程式碼塊才可以通過變數名訪問該變數。
  • 如果在內層程式碼塊定義了一個與外層程式碼塊變數同名的變數,稱之為內層定義覆蓋了外部定義,但當程式退出內層程式碼塊是,外部變數重新恢復使用。
  • 除非顯式地初始化自動變數,否則它不會被自動初始化。

12.1.5  暫存器變數

int main(void)
{
        register int quick;        //暫存器變數
  • 使用儲存類說明符 register 可以宣告暫存器變數。

12.1.6  具有程式碼塊作用域(空連結)的靜態變數

int main(void)
{
        static int stay;        //具有程式碼塊作用域的靜態變數
  • 使用儲存類說明符 static 在程式碼塊內宣告可以建立具有程式碼塊作用域(空連結)的靜態變數。
  • stay 只在編譯時初始化一次。
  • 如果不顯式地對靜態變數進行初始化,它們將被初始化為0。
  • 對函式參量不能使用 static,例如:int work(static int flu);  //不允許

12.1.7  具有外部連結的靜態變數

int Errupt;        //定義宣告
extern char Coal;  //其他檔案中定義的變數

int main(void)
{
        extern int Errupt;    //引用宣告
        ...
  • 在所有函式外部宣告可以建立具有外部連結的靜態變數。
  • 具有外部連結的靜態變數可以被程式的任一檔案中包含的函式使用。
  • 如果變數是在別的檔案中定義的,則必須使用 extern 來宣告該變數。
  • 一個外部變數只可進行一次初始化,且一定是在變數定義處進行。
  • 如果不顯式地對外部變數進行初始化,它們將被初始化為0。
  • 只可以用常量表達式來初始化檔案作用域變數。
  • 變數 Errupt 聲明瞭兩次,第一次宣告稱為定義宣告,第二次宣告稱為引用宣告。extern 表明該宣告不是一個定義,它指示編譯器參考其他地方。
  • 這一儲存型別又被稱為外部儲存類,這一型別的變數被稱為外部變數。

12.1.8  具有內部連結的靜態變數

static int svil = 1;        //具有內部連結的靜態變數

int main(void)
{
  • 使用儲存類說明符 static 在所有函式外部宣告可以建立具有內部連結的靜態變數。
  • 具有內部連結的靜態變數只可以被與它同文件中的函式使用。

12.1.9  多檔案

  • 複雜的 C 程式往往使用多個獨立的程式碼檔案。ANSI C 通過在一個檔案中定義變數,在其他檔案中引用宣告這個變數實現共享。
  • 除非在第二個檔案中通過引用宣告(使用 extern)聲明瞭該變數,否則在第一個檔案中定義的外部變數在第二個檔案中不可用。

12.2  儲存類說明符

  • 不可以在一個宣告中使用一個以上儲存類說明符
auto 只能用在具有程式碼塊作用域的變數宣告中:自動變數
register 只能用在具有程式碼塊作用域的變數宣告中:暫存器變數
static 用與具有程式碼塊作用域的變數的宣告時,表明該變數具有靜態儲存型別:具有空連結的靜態變數
用與具有檔案作用域的變數的宣告時,表明該變數具有內部連結:具有內部連結的靜態變數
extern 若包含 extern 的宣告具有檔案作用域(引用宣告),所指向的變數必然具有外部連結:具有外部連結的靜態變數
若包含 extern 的宣告具有程式碼塊作用域(引用宣告) 所指向的變數可能具有外部連結:具有外部連結的靜態變數
所指向的變數可能具有內部連結:具有內部連結的靜態變數

12.3  儲存類和函式

函式分類:

  • 外部函式(預設):可被其他檔案中的函式呼叫。例:double gama();
  • 靜態函式:只可以在定義它的檔案中使用。例:static double delta();
  • 行內函數:

說明:

  • 在一個檔案中定義了一個靜態函式 fun1() 時,仍可以在其他檔案中定義和使用與靜態函式 fun1() 具有相同名稱的不同函式。
  • 通常使用關鍵字 extern 來宣告在其他檔案中定義的函式。
  • 儘可能保持每個函式的內部工作對該函式的私有性,只共享那些需要共享的變數。

12.4  隨機數函式和靜態變數

ANSI C 程式庫提供了隨機數函式 rand(),並提供了一個可移植的標準演算法來產生隨機數。事實上 rand() 是一個“偽隨機數發生器”,這種方案始於一個“種子” 的數字,函式使用這個種子來產生一個新數,而這個新數又稱為新的種子。因此隨機數函式必須記下上次被呼叫時所使用的種子,因此需要一個靜態變數。但如果每次執行時都從同一個種子開始,函式產生的隨機數就不“隨機”了。因此需要通過一個重置種子的函式 srand()。

12.6  分配記憶體:malloc() 和 free()

  • 函式 malloc() 接受一個引數:所需記憶體位元組數。然後 malloc() 找到可用記憶體中一個大小合適的塊。
  • malloc() 分配了記憶體,並返回那塊記憶體第一個位元組的地址。將該地址賦值給一個指標變數,就可使用該指標來訪問那塊記憶體。
  • ANSI C標準規定,將malloc() 返回值定義為指向 void 的指標型別,這一型別被用作“通用指標”。也可以對指標進行型別指派。
  • 如果 malloc() 找不到所需的記憶體塊,它將返回空指標。
double *ptd;
ptd = (double *)malloc(30 * sizeof(double));    //請求30個double型別值得空間,並使ptd指向該空間所在位置
  • 可以使用表示式 ptd[0] 來訪問記憶體塊的第一個元素,以此類推。

一般地,對應每個 malloc() 呼叫,應呼叫一次 free()。

  • 函式free() 接受一個引數:之前 malloc() 返回的地址。然後 free() 釋放先前分配的記憶體。這樣所分配記憶體的持續時間從呼叫 malloc() 分配記憶體開始,到呼叫 free() 釋放記憶體供再使用為止。
  • 程式還可呼叫函式 exit(),用來在記憶體分配失敗時結束程式。exit(EXIT_FAILURE) 表示程式異常終止。
  • 函式 malloc() 、free()、exit() 在標頭檔案 stdlib.h 中定義。

12.6.1  free() 的重要性

程式呼叫 malloc(),被分配的記憶體所使用的記憶體數量只會增加,造成記憶體洩漏,通過在函式末尾處呼叫 free() 可防止該問題。

12.6.2  函式 calloc()

  • 函式 calloc() 接受兩個引數:第一個引數是所需記憶體單元的數量,第二個引數是每個單元以位元組計的大小。引數都是無符號整數。
  • calloc() 分配一塊記憶體,並返回一個 void 指標。
  • calloc() 將塊中的全部位都置為0。
  • free() 也可用來釋放由 calloc() 分配的記憶體。

12.6.4  儲存類與動態記憶體分配

記憶體可分為三個獨立的部分:

  • 具有外部連結的、具有內部連結的、具有空連結的靜態變數的記憶體。——記憶體在程式開始執行時被分配,並在整個程式執行期間一直存在。
  • 自動變數的記憶體。——變數所用記憶體在程式執行到該變數所在程式碼塊時開始分配,在退出程式碼塊時釋放。
  • 動態分配的記憶體。——在呼叫 malloc() 或相關函式時產生,在呼叫函式 free() 時釋放。

12.7  ANSI C 的型別限定詞

一個變數是以它的型別和儲存類表徵的。

const 將資料限定為不變的,不能通過賦值、增量或減量運算來修改變數值
volatile 資料除了可被程式改變以外還可被其他代理改變
restrict 只可用於指標,表明指標是訪問一個數據物件的惟一且初始的方式 告訴編譯器可以自由地做一些有關優化的假定
告訴使用者僅使用滿足 restrict 要求的引數