首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C程序記憶體佈局完整指南

     編輯:關於C語言

C程序記憶體佈局完整指南

理解 C 程序在記憶體中的分佈,是寫出高效、穩健代碼的前提。本文從作用域、生命週期、用戶空間視角、運行時段五個維度,系統闡述變量與代碼在記憶體中的安置規則。

一、從作用域(空間)劃分

  • 全域變量:函數外部定義,整個文件從定義處至結尾可見。
  • 區域變量:函數/塊內部定義,僅在所屬塊內可見。

二、從生命週期(生存期)劃分

2.1 靜態儲存區

程序啟動時分配,結束時釋放,地址固定,不動態申釋。包含四大段:

段名內容特徵
代碼段機器指令、字串常量、const 全域/區域變量唯讀,共享
只讀資料段const 修飾的全域/區域變量、字面常量唯讀,編譯期確定
讀寫資料段已初始化非零全域變量、static 區域變量讀寫,有初值
BSS 段未初始化或初始化為 0 的全域/靜態區域變量啟動時清零,不佔文件空間

注意:const char a[100]="ABCDEFG" 產生 100 位元組唯讀區;const char a[]="ABCDEFG" 僅 8 位元組(含 \0)。唯讀區通常需完全初始化。

2.2 動態儲存區

函數調用時分配,返回時釋放。包含:

  • 函數形式參數
  • 自動變量(無 static 的區域變量)
  • 函數調用現場保護(返回地址、保存的寄存器等)

三、用戶空間三大區

  1. 程序區:一行行待執行的機器碼。
  2. 靜態儲存區:上述四大段。
  3. 動態儲存區:堆與棧。

四、運行時精細段劃分

1. 代碼段

CPU 指令計數器(PC)指向此處順序執行機器碼。

2. 只讀資料段

存放運行期不變的數據:#define 常量、const 全域/區域變量、字串字面值。編譯期確定。

3. 已初始化讀寫資料段

已初始化全域變量、static 區域變量。程序啟動分配,有初值,可讀寫。

4. 未初始化資料段

未初始化全域變量、static 區域變量。啟動前不佔空間,載入時由系統清零。

5. 堆

運行時出現,程序員 malloc/free 手動管理。OS 可能在進程結束時回收未釋放內存。p = malloc(10) 分配的 10 位元組即在堆上。

6. 棧

運行時出現,編譯器自動管理。存放:函數內部動態變量、函數參數、返回值。動態增減:調用層次深則棧增長;大結構體傳參/返回佔用更多棧空間。

五、四大儲存類別關鍵字詳解

5.1 static

  • 修飾區域變量:儲存於靜態區,生命週期 = 程序全程,作用域僅限函數內,編譯期賦初值(僅一次),未初始化自動為 0。函數不可返回此類變量指針(雖然地址有效,但違反封裝)。
  • 修飾全域變量/函數:限定本編譯單元可見(內部鏈結),隱藏實現細節,利於大型專案模組化。
int f(int a) {
    auto int b = 0;      // 隱式 auto,棧上,每次調用重新初始化
    static int c = 3;    // 靜態區,僅初始化一次,保持值
    b++; c++;
    return a + b + c;
}

5.2 extern

聲明外部變量/函數(定義在別處),不分配空間,僅擴展作用域至聲明處至文件末尾。跨文件共享全域變量:一個文件定義,其餘 extern 聲明。

// file1.c
int A = 13, B = -8;

// file2.c
extern int A, B;  // 聲明,不定義
int max(int x, int y) { return x>y?x:y; }
int main() { printf("%d\n", max(A,B)); }

5.3 auto

區域自動變量(含形參),棧上動態分配,調用時創建,返回時銷毀。auto 可省略,不寫即隱含自動儲存類。未初始化值不確定(髒值)。

5.4 register

建議編譯器將區域自動變量/形參放入 CPU 寄存器,提高頻繁訪問效率。限制:寄存器數量有限,無法取地址(&),靜態區域變量不可用。現代編譯器優化極佳,通常無需手動指定。

六、總結對照

分類屬於產生時機釋放時機
代碼段、只讀段、讀寫段靜態區鏈接後程序結束
BSS 段靜態區程序初始化程序結束
堆、棧動態區運行時按需運行時釋放

映像文件(.out/.elf)含:代碼段、只讀段、讀寫段(稱 Section)。運行時進程含:上述三段 + BSS + 堆 + 棧(稱 Segment)。

七、綜合實例分析

const char ro[] = "this is read only data";           // 只讀數據段
static char rw_1[] = "this is global read write data"; // 讀寫數據段(全域)
char BSS_1[100];                                       // BSS 段(全域未初始化)
const char *ptrconst = "constant data";                // 字串在只讀段,ptrconst 在讀寫段/BSS

int main() {
    short b;                          // 棧,2 位元組
    char a[100];                      // 棧,100 位元組
    char s[] = "abcdefg";             // s 在棧(8 位元組),"abcdefg" 在只讀段(8 位元組)
    char *p1;                         // 棧,指針大小
    char *p2 = "123456";              // p2 在棧,"123456" 在只讀段
    static char rw_2[] = "local rw";  // 讀寫數據段(局部靜態)
    static char BSS_2[100];           // BSS 段(局部靜態未初始化)
    static int c = 0;                 // 讀寫數據段(全域靜態初始化)
    p1 = malloc(10);                  // 堆分配 10 位元組
    strcpy(p1, "xxxx");               // "xxxx" 在只讀段(5 位元組含\0)
    free(p1);                         // 釋放堆內存
    return 0;
}

關鍵差異說明:

  • rw_1 無論是否 static 皆在讀寫段,static 僅影響跨文件可見性;rw_2 若無 static 則成棧上區域變量,語義全變。
  • BSS_1(全域)全程序可見,BSS_2(局部靜態)僅函數內可見。BSS 段必須顯式指定大小(如 [100]),編譯器據此擴展 BSS 長度。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved