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

C 語言深度探險:從編譯環境到函數地址解析

     編輯:關於C語言

C 語言深度研究筆記(一):編譯鏈接與函數地址本質

本系列記錄從零構建 TC2.0 編譯環境、探究編譯鏈接流程、驗證記憶體模型,到解析函數地址與變量存儲的真實機制。旨在透過動手實驗,建立對 C 語言底層運行模型的直觀理解。

一、構建極簡 TC2.0 編譯環境

從 TC2.0 安裝目錄提取核心檔案:

  • TCC.EXE - 編譯器
  • TLINK.EXE - 連結器
  • C0S.OBJ - 啟動代碼
  • CS.LIB, EMU.LIB, MATHS.LIB - 標準庫

將上述檔案置於同一目錄 (如 C:\C),即可獨立編譯連結。實驗證明:

  1. TCC 內部自動調用 TLINK 完成連結
  2. 刪除 TLINK 會導致連結失敗,確認其被 TCC 呼叫
  3. 手動執行 TLINK 可將單獨的 .OBJ 連結為 .EXE

二、記憶體模型驗證:Small Model

TC2.0 Small Model 特性:

  • 代碼段 (CS) 獨立 64KB
  • 資料段 (DS) 與堆疊段 (SS) 合併共用 64KB

驗證程式:


#include 
void f1() { int a=1; }
void f2() { int b=2; }
int main() {
    printf("CS=%X DS=%X SS=%X\n", _CS, _DS, _SS);
    printf("f1=%p f2=%p\n", f1, f2);
    return 0;
}

結果顯示 DS == SS,CS 不同,印證 Small Model 記憶體佈局。函數名直接作為地址常量使用 (如 printf("%p", f1)) 可正常編譯輸出入口偏移地址。

三、函數地址與段地址獲取

  • 偏移地址:直接用 &func 或函數名 func 取得 (近指標,16 位)
  • 段地址 (CS):透過內嵌彙編或偽變量 _CS 讀取

// 顯示 CS 段地址
printf("CS = %X\n", _CS);  // TC2.0 偽變量
// 或內嵌彙編
asm { mov ax, cs; mov [cs_val], ax; }

實驗發現:直接在 CMD 執行 .EXE 與在 DEBUG 載入執行時的 CS 值不同,因 DEBUG 會將程式載入固定段地址。

四、變量地址本質:全域 vs 區域

初步觀察到區域變量地址顯示為負值 (如 -28),後經驗證:

  • 全域/靜態變量:地址為資料段內的絕對偏移量 (正值)
  • 區域變量:%d 格式輸出時,實為相對於當前堆疊頂 (SP) 的有號偏移量。實際地址 = SS:SP + 偏移量

重要修正:原筆記結論有誤。使用 %p 或 %x 可見區域變量真實偏移地址為正值 (如 0xFFE4)。負值現象源於 %d 將無號 16 位值解釋為有號整數。區域變量地址本質仍是堆疊段內的偏移量,非相對位置編碼。

五、心得與延伸

  1. 精簡環境揭示核心機制:編譯器、連結器、啟動代碼、運行庫四大件即可構建可執行程式
  2. 檔案與記憶體本質相同:皆為二進制位元組序列,區別在於 CPU 解釋方式 (代碼/資料/堆疊)
  3. 段地址固定、偏移 0~FFFF 限定 64KB 邊界,是 x86 實模式架構的直接體現

推薦進階書籍

  • 《The C Programming Language (K&R)》:C 之父經典,薄而精煉,必讀
  • 《C 專家編程》:深入鏈接、記憶體、預處理器等進階主題
  • 《C 指針》:系統性講解指針與記憶體模型
  • 《C 陷阱與缺陷》:避坑指南,邊界情況詳解

建議大一新生先啃完 K&R 並上機完成所有習題,再進階算法與資料結構。

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