深度解析C語言中的變量作用域、鏈接和存儲期的含義
在c中變量有三種性質:
1、存儲期限:變量的存儲期限決定瞭變量占用的內存空間什麼時候會被釋放,具有動態存儲期限的變量會在所屬的程序塊被執行時獲得內存空間,在結束時釋放內存空間。具有靜態存儲期限的變量在程序運行的整個期間都會占用內存空間。
2、作用域:變量有塊作用域也有文件作用域,結合序章第一張圖可以明白塊作用域是在某些程序塊內起作用,文件作用域是在整個c文件之內起作用。
3、鏈接:鏈接是各個文件之間的關系,具有內部鏈接的變量隻在本文件內起作用,具有外部鏈接的變量可以在不同文件內起作用。具有無鏈接的變量隻在某一個文件的某個函數內起作用。
C變量作用域、鏈接和存儲期的含義
錯誤的定義C語言變量的作用域、鏈接和存儲區在某種程度上會直接影響程序的設計,我們通過這篇博文介紹如何正確合理的定義一個C變量。
從定義一個C變量開始成為一個C高手
一、基本術語
1.1 什麼是對象(object)
C語言中的對象含義與C++的完全不同,C語言中的對象指的是存儲數據的一塊內存。對象可以存儲一個或多個值,一個對象可能並未存儲實際的值,但是在存儲適當的值時一定具有相應的大小。對象可以存在於程序的執行期,也可以僅存在於它所在函數的執行期。
1.2 什麼是標識符(identifier)
標識符就是一個名稱,通過這個標識符可以修改對象的內容。標識符可以在源代碼的多文件中共享、可用於特定文件的任意函數中、可僅限於特定函數中使用,甚至隻在函數中的某部分使用。
舉個例子,我們定義一個int型變量point:
int point;
對象與標識符的關系如下圖
二、作用域、鏈接和存儲期三者關系
存儲期用來描述對象,所謂的存儲期就是指對象在內存中可以保留多長時間。
標識符用於訪問對象,使用作用域(scope)和鏈接(linkage)描述標識符,標識符的作用域和鏈接表明瞭程序的哪些部分可以使用它。
三、作用域詳解
作用域描述程序中可訪問標識符的區域
一個C變量的作用域可以是塊作用域、函數作用域、函數原型作用域和文件作用域,下面我們詳細講解每個作用域的含義。
3.1 塊作用域
塊是用一對花括號括起來的代碼區域,定義在塊中的變量具有塊作用域,塊作用域變量的可見范圍是從定義處到包含該定義的塊的末尾。
另外,雖然函數的形式參數聲明在函數的左花括號之前,但它們也具有塊作用域,屬於函數體這個塊。
int Star_CongShanRuoShui(int user_id) { int res = 0; ...... return }
👆上面code中“user_id”和“res”都具有塊作用域
int Star_CongShanRuoShui(int user_id[] , int n) { int res = 0; for(int i=0;i<n;++i) { ...... int q=0; //q的作用域開始 ...... } //q的作用域結束 ...... return }
👆上面code中變量“q”的作用域僅限於for循環的循環體中
3.1.1 塊概念的擴展
C99將塊概念擴展到包括for循環、while循環、do while循環和if語句所控制的代碼,即使這些代碼沒有使用花括號括起來,也算是塊的一部分,下面的code👇
#include<stdio.h> int main() { int Star_CSRS = 8; printf("[main] value of Star_CSRS is %d | address of Star_CSRS:%p\n", Star_CSRS ,&Star_CSRS); for(int Star_CSRS=0;Star_CSRS<4;++Star_CSRS) { printf("[for index] value of Star_CSRS is %d | address of Star_CSRS:%p\n", Star_CSRS ,&Star_CSRS); int Star_CSRS = 6; printf("[for] value of Star_CSRS is %d | address of Star_CSRS:%p\n", Star_CSRS ,&Star_CSRS); ++Star_CSRS; } printf("[main] value of Star_CSRS is %d | address of Star_CSRS:%p\n", Star_CSRS ,&Star_CSRS); return 0; }
輸出如下:
- 在main中定義瞭變量Star_CSRS,在for循環頭中定義的變量Star_CSRS 隱藏瞭main中的Star_CSRS
- for的循環體中的Star_CSRS又隱藏瞭for循環頭中的Star_CSRS
3.2 函數作用域
函數作用域的概念僅限於goto語句的標簽。這意味著即使一個標簽首次出現在函數的內層塊中,它的作用域也延伸至整個函數中(詳細可以參考我的這篇博文)。
3.3 函數原型作用域
用於函數原型的形參變量名,如下所示:
int Star_CongShanRuoShui(int user_id);
函數原型作用域的范圍是從形參定義處到原型聲明結束。對於函數原型中的形參編譯器隻關心形參類型,不關系具體的形參名,即使有形參名也不必與函數定義中的形參名相匹配。
隻有在變長數組中,形參名才有意義,如下例(變長數組詳細參考我的這篇博文):
int Star_CongShanRuoShui(int n ,int user_id[n]);
3.4 文件作用域
定義在所有函數外的變量具有文件作用域。具有文件作用域的變量,從它的定義處到該定義所在文件的末尾均可見(文件作用域變量也稱為全局變量)。
翻譯單元與文件
編譯器將一個源代碼文件和所有的頭文件都看成一個包含信息的單獨文件,這個文件被稱為翻譯單元一個翻譯單元包括一個源代碼文件和它所include的文件如果一個程序有多個源代碼文件,那麼這個程序也將有多個翻譯單元一個文件作用域變量的可見范圍其實是整個翻譯單元(一個源代碼文件+頭文件)
四、鏈接詳解
C變量有3中鏈接屬性:無鏈接、內部鏈接、外部鏈接
4.1 無鏈接
具有塊作用域、函數作用域和函數原型作用域的變量都是無鏈接變量。這意味著這些變量屬於定義它們的塊、函數或原型私有。
4.2 內部鏈接
內部鏈接變量隻能在一個翻譯單元中使用,該變量使用存儲類別說明符static修飾,如下:
int Star_CSDN=1; //文件作用域,外部鏈接 static int Star_CongShanRuoShui=2; //文件作用域,內部鏈接 int main() { ...... return 0 }
4.3 外部鏈接
外部鏈接變量可以在多個文件中使用。外部鏈接變量的聲明分為“定義性聲明”和“引用性聲明”。C編譯器要求一個變量隻能定義一次,重復定義編譯器會報錯。如果需要在其它文件中使用外部鏈接變量需要使用extern引用性聲明這個變量,如下面cdoe:
//file a.c //在文件a.c中定義一個外部鏈接變量Star_CongShanRuoShui int Star_CongShanRuoShui = 2; ......
//file b.c //文件b.c中使用extern引用性聲明變量Star_CongShanRuoShui extern int Star_CongShanRuoShui; ......
五、存儲期詳解
C對象有4種存儲期:靜態存儲期、線程存儲期、自動存儲期、動態分配存儲期。
5.1 靜態存儲期
具有靜態存儲期的對象,它在程序的執行期間一直存在。文件作用域變量具有靜態存儲期
註意,對於文件作用域變量,關鍵字static表明瞭其鏈接屬性,而非存儲期。以static聲明的文件作用域變量具有內部鏈接屬性,但是無論是內部鏈接還是外部鏈接,所有文件作用域變量都具有靜態存儲期。
5.2 線程存儲期
線程存儲期用於並發程序設計,程序執行可被分為多個線程。具有線程存儲期的對象,從被聲明時到線程結束一直存在。以關鍵字__Thread_local聲明一個對象時,每個線程都獲得這個變量的私有備份。
5.3 自動存儲期
塊作用域的變量通常都具有自動存儲期。當程序進入定義這些變量的塊時,為這些變量分配內存;當退出這個塊時,釋放剛才為這些變量分配的內存。通過這種做法可以實現內存的重復利用。
變長數組稍有例外,它的存儲期從聲明處到塊的末尾,而不是從塊的開始處到塊的末尾
我們上面說塊作用域的變量通常都具有自動存儲期,但是也能具有靜態存儲期。為瞭創建這樣的變量,要把變量聲明在塊中,且在聲明前加上關鍵字static,如下例:
int Star_CongShanRuoShui(int user_id) { static int isStar = 0; ...... return }
變量isStar存儲在靜態內存中,它從程序被載入到程序結束期間都存在。但是,它的作用域定義在Star_CongShanRuoShui()函數塊中,隻有在執行該函數時,程序才能使用isStar訪問它所指定的對象(當然,也可以存儲該變量的地址實現間接訪問該對象)
5.4 動態分配存儲期
程序運行時通過malloc()等內存分配函數分配的對象具有動態分配存儲期,這樣的對象需要使用free()函數進行銷毀。
動態內存分配和變長數組在功能上有些重合,但是還是有所不同:
變長數組是自動存儲類型用malloc函數創建的數組不必局限在一個函數中使用
以上就是深度解析C語言中的變量作用域、鏈接和存儲期的含義的詳細內容,更多關於C語言變量作用域、鏈接和存儲期的資料請關註WalkonNet其它相關文章!