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

C/C++ 程式記憶體佈局完整圖解:從 .text 到 Stack/Heap 深度解析

     編輯:關於C

五大記憶體區段總覽

區段英文內容權限生命週期管理者
程式碼區.text函數機器碼、常數字串字面值R/X程式全期作業系統載入器
唯讀資料區.rodataconst 全域/靜態變數、字串常值R程式全期作業系統
已初始化資料區.data / .rwdata非零初始化的全域/靜態變數R/W程式全期作業系統
未初始化資料區.bss零初始化的全域/靜態變數(編譯器自動清零)R/W程式全期作業系統
堆區heapmalloc/new 動態分配記憶體R/W手動控制程式設計師 / 配置器
棧區stack區域變數、函數參數、返回位址、暫存器保存R/W函數呼叫期間編譯器自動

虛擬記憶體佈局圖(典型 32-bit Linux,高位址在上)

+------------------------+  0xFFFFFFFF (4GB)
|  Kernel Space (1GB)    |  用戶程式不可直接存取
+------------------------+  0xC0000000
|        Stack ↓         |  向下增長(高位址 → 低位址)
|                        |
|                        |
|                        |
|        Heap ↑          |  向上增長(低位址 → 高位址)
|                        |
+------------------------+  .bss 結束
|      .bss (zero)       |
+------------------------+  .data 結束
|      .data (init)      |
+------------------------+  .rodata 結束
|      .rodata           |
+------------------------+  .text 結束
|      .text (code)      |
+------------------------+  0x08048000 (典型載入基址)

實際程式碼對應表


// main.cpp
int a = 0;                 // .data (已初始化全域)
char *p1;                  // .bss  (未初始化全域)

int main() {
    int b;                 // Stack (未初始化區域)
    char s[] = "abc";      // Stack (陣列內容複製自 .rodata)
    char *p2;              // Stack (指標變數本身)
    char *p3 = "123456";   // p3 在 Stack,"123456\0" 在 .rodata
    static int c = 0;      // .data (已初始化靜態區域)

    p1 = malloc(10);       // Heap (10 bytes)
    p2 = malloc(20);       // Heap (20 bytes)
    strcpy(p1, "123456");  // 複製字串常值到 Heap,編譯器可能合併相同常值
    free(p1); free(p2);    // 歸還 Heap
    return 0;
}

Stack vs Heap 深度比較

維度Stack(棧)Heap(堆)
管理方式編譯器自動( prologue/epilogue 指令)手動 malloc/free 或 new/delete
空間大小有限(Linux 預設 8MB,Windows 1MB,可調整)巨大(32-bit 理論 4GB,受虛擬記憶體限制)
碎片問題無(LIFO,永不產生洞)嚴重(頻繁分配釋放產生外部碎片)
增長方向向下(位址遞減)向上(位址遞增)
分配速度極快(指標加減)較慢(尋找合適塊、可能系統調用)
失敗模式Stack Overflow(遞迴過深、大型區域陣列)Allocation Failure(記憶體耗盡、碎片化)

Global vs Static 細節補充

static 全域變數 vs 普通全域變數

  • 儲存方式相同(皆在 .data/.bss)。
  • 差異在於連結屬性:static 為內部連結,僅本編譯單元可見;普通全域為外部連結,全專案可見(需 extern 宣告)。
  • 善用 static 減少符號汙染,避免多檔案命名衝突。

static 區域變數 vs 普通區域變數

  • 儲存位置不同:前者在 .data/.bss,後者在 Stack。
  • 生命週期不同:前者程式全期,後者函數呼叫期間。
  • 初始化時機:前者程式啟動前(靜態初始化),後者每次進入函數執行。

static 函數

同 static 全域變數,限制函數作用域於本編譯單元,實現封裝。注意:static 此處非指儲存類別,而是連結屬性。

常見錯誤與最佳實踐

  • ❌ 在 Stack 配置大型陣列(char buf[1000000])→ Stack Overflow。✅ 改用 malloc 或 static。
  • ❌ 返回區域變數指標/參考 → 野指標。✅ 靜態區域、呼叫端提供緩衝區、或動態分配。
  • ❌ Heap 記憶體洩漏。✅ RAII 風格封裝、智慧指標(C++)、嚴格配對 malloc/free。
  • ❌ Stack/Heap 緩衝區溢位。✅ 邊界檢查、安全函式(snprintf)、編譯器強化(-fstack-protector、FORTIFY_SOURCE)。

總結

深入理解記憶體佈局是 C/C++ 系統程式設計師的必修課。從編譯連結產生的 Section,到執行期 Stack/Heap 的動態互動,再到 static/extern 的連結語義,每一層都直接影響程式的正確性、效能與安全性。建議搭配 size、nm、objdump、/MAP 等工具觀察實際輸出,理論結合實驗,方能臻於化境。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved