【粉絲問答10】C語言關鍵字static的使用詳解
阿新 • • 發佈:2021-03-04
視訊地址:https://www.ixigua.com/6935761378816819748
# 粉絲提問
粉絲問題,總結一下:
關鍵字static的使用方法。
![問題](https://img-blog.csdnimg.cn/20210118203352671.jpg)
要想搞清楚關鍵字static的使用方法,必須首先搞清楚,可執行程式段的分類以及各段在記憶體區的邏輯地址的對映。
# 一、可執行程式記憶體分配
## 1. 可執行程式程式分段
一個程式的3個基本段:text段,data段,bss段
![ ](https://img-blog.csdnimg.cn/20210121195810736.png)
1. BSS
BSS(Block Started by Symbol)通常是指用來存放程式中未初始化的全域性變數和靜態變數的一塊記憶體區域。
特點是:可讀寫的,在程式執行之前BSS段會自動清0。
所以,未初始的全域性變數在程式執行之前已經成0了。
注意和資料段的區別,BSS存放的是未初始化的全域性變數和靜態變數,資料段存放的是初始化後的全域性變數和靜態變數。
UNIX下可使用size命令檢視可執行檔案的段大小資訊。如size a.out。
2. 資料段.data
存放在編譯階段(而非執行時)就能確定的資料,可讀可寫。
也就是通常所說的靜態儲存區,賦了初值的全域性變數和賦初值的靜態變數存放在這個區域,常量也存在這個區域。資料段,程式碼段在程式執行之前就已經確定了的。
3. 程式碼段.text
程式碼段通常是指用來存放程式執行程式碼的一塊記憶體區域。
這部分割槽域的大小在程式執行前就已經確定,並且記憶體區域通常屬於只讀, 某些架構也允許程式碼段為可寫,即允許自修改程式。
在程式碼段中,也有可能包含一些只讀的常數變數,例如字串常量等。
text段在編譯時確定,記憶體中被對映為只讀,但date段與bss段是可寫的。
## 2. c語言五大記憶體分割槽
1. 棧區(堆疊區stack)
堆疊是由編譯器自動分配釋放,存放函式的引數和區域性變數的值(auto型別),操作方式類似於資料結構中的棧。棧的申請是由系統自動分配,如在函式內部申請一個區域性變數int h,同時判斷所申請空間是否小於棧的剩餘空間,如果小於則為其開闢空間,為程式提供記憶體,否則將報異常提示棧溢位。
2. 堆(heap)
堆一般由程式設計師分配釋放,若程式設計師不釋放,程式結束可能由OS回收。
它與資料結構中的堆是兩回事,分配方式類似於連結串列,申請則是程式設計師自己操作使用malloc或new。
申請過程比較複雜,當系統收到程式的申請時,會遍歷記錄空閒記憶體地址的連結串列,以求尋找第一個空間大於所申請空間的堆節點,然後將該節點從空閒節點連結串列中刪除,並將該節點的空間分配給程式,有些情況下,新申請的記憶體塊的首地址記錄本次分配的記憶體塊的大小,這樣在free()時能正確的釋放記憶體空間。
3. 全域性靜態儲存區
全域性變數與靜態變數的儲存是放在一塊的,初始化的全域性變數與靜態變數存放在一塊區域,未初始化的全域性變數與未初始化的靜態變數存放在相鄰的另一塊區域。
4. 文字常量區
常量字串就是放在該部分,只讀儲存區,程式結束後由系統釋放
5. 程式程式碼區
存放程式的二進位制程式碼區。
![ ](https://img-blog.csdnimg.cn/20210121194935966.png)
兩者之間區別是:程式碼段,資料段,堆疊段是cpu級別的概念,五大分割槽屬於語言級別的概念,兩者是不同的概念。
## 3. 可執行程式記憶體空間與邏輯地址空間的對映與劃分
![ ](https://img-blog.csdnimg.cn/20210121200608613.png)
左邊是UNIX系統的執行檔案,右邊是程序對應的邏輯地址空間的劃分情況
## 4. 舉例
![ ](https://img-blog.csdnimg.cn/20210121201428908.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Rhb2Nhb2thZmVp,size_16,color_FFFFFF,t_70)
# 二、static 變數
static變數主要區分靜態全域性變數和全域性變數、區域性變數和靜態區域性變數之間的區別。
## 1. 靜態全域性變數、全域性變數
靜態全域性變數、全域性變數的區別主要通過**生存週期和作用域**來區別。
| |全域性變數 | 靜態全域性變數 |
|--|:--|:--|
| 生存週期 | 程式執行到程式結束 |程式執行開始到程式結束 |
|作用域 | 所有的程式碼 | 只有當前檔案可以訪問 |
| 程式碼段中位置 | 全域性資料區 | 全域性資料區|
- a.靜態全域性變數和全域性變數均存放在資料段.data中;
- b. 靜態區域性變數在函式內定義,生存期為整個源程式,但作用域與自動變數相同,只能在定義該變數的函式內使用。退出該函式後, 儘管該變數還繼續存在,但不能使用它。
- c. 對基本型別的靜態區域性變數若在說明時未賦以初值,則系統自動賦予0值。而對自動變數不賦初值,則其值是不定的。
- d.全域性變數本身就是靜態儲存方式, 靜態全域性變數當然也是靜態儲存方式。
但是他們的作用域,非靜態全域性 變數的作用域是整個源程式(多個原始檔可以共同使用);
而靜態全域性變數則限制了其作用域, 即只在定義該變數的原始檔內有效, 在同一源程式的其它原始檔中不能使用它。
### 全域性變數例項
以下是b.c 和 a.c原始碼
![全域性變數](https://img-blog.csdnimg.cn/2021012121540853.png)
編譯
```c
gcc a.c b.c
```
執行結果:
![執行結果](https://img-blog.csdnimg.cn/20210121215452546.png)
由編譯結果可知,檔案a.c可以訪問到b.c檔案中的靜態全域性變數b。
### 靜態全域性變數例項
![靜態全域性變數 ](https://img-blog.csdnimg.cn/20210121215707675.png)編譯結果
![編譯結果](https://img-blog.csdnimg.cn/202101212157479.png)
由編譯結果可知,檔案a.c無法訪問到b.c檔案中的靜態全域性變數b,所以編譯報錯。
## 2. 靜態區域性變數、區域性變數
靜態區域性變數、區域性變數的區別主要通過**生存週期和作用域**來區別。
| |區域性變數 | 靜態區域性變數 |
|--|--|--|
| 生存週期 | 函式呼叫到函式返回 |程式執行開始到程式結束 |
| 作用域 | 函式內部 | 函式內部 |
| 程式碼段中位置 | 棧 | 全域性資料區|
靜態區域性變數存放在資料段.data中,區域性變數在棧中;
靜態區域性變數和區域性變數都只能在函式體內部才可以訪問。
函式每次訪問的靜態區域性變數,該變數的值為最後一次訪問修改後的值。
舉例:
```c
1 #