Turbo C 2.0 (16位實模式),Debug 反汇編觀察。段寄存器:CS(代碼)、DS(數據)、SS(棧)、ES(附加)。
int n=0, a, b, c;
int f1() { return 1; }
int f2(int x, int y) { int c; return x+y; }
int f3(int x, int y) { return x+y; } // 遠調用
void main() { n = f1(); a = f2(1,2); b = f3(3,4); }
| 實體 | 存儲段 | 地址形式 | 生命週期 |
|---|---|---|---|
| 全局變量 n,a,b,c | DS (數據段) | DS:offset (如 DS:01A6) | 程序啟動分配,結束釋放 |
| 局部變量 (f2 中 c) | SS (棧段) | BP+offset (如 BP-2) | 函數調用分配,返回釋放 |
| 參數 x,y | SS (棧段) | BP+offset (壓棧順序:右至左) | 調用者壓棧,調用者清棧 (CDECL) |
| 返回值 | AX 寄存器 | 16位值直接放 AX | 調用約定規定 |
; 調用者
push 2 ; 參數 y (右至左)
push 1 ; 參數 x
call f2
add sp, 4 ; 調用者清棧
; f2 入口
push bp
mov bp, sp
sub sp, 2 ; 分配局部變量 c 空間
; ... 訪問參數 [bp+4], [bp+6]
; 返回值放入 AX
mov sp, bp
pop bp
ret
f1:call near ptr (段內),返回 ret (僅彈 IP)。f3:call far ptr (段間),返回 retf (彈 CS:IP)。far 關鍵字或編譯模型 (Large/Huge)。int a=0; void f() { int n=2; n++; } // n 被優化至寄存器 SI,a 在數據段固定位置
編譯器將 n=2; n++ 合併為 mov si, 3,n 全程駐留寄存器,不佔棧空間。啟示:Debug 模式下變量才嚴格按語義存儲;Release 優化會打破「局部變量必在棧」的假設。
int a; char b; long c; // 地址連續:a(2B), b(1B), c(4B) -> 差值 2,1,2
a++; b++; c++;
int/char:inc word ptr [bp-x] / inc byte ptr [bp-y]long:add word ptr [bp-z], 1 + adc word ptr [bp-z+2], 0 (模擬 32 位加法)TC 2.0 類型大小:char=1, int=2, long=4, float=4, double=8。結構體成員按聲明順序連續排列,無填充 (16位模式下)。
struct stu { int id; char name[3]; } a; // 全局 -> DS
void f() { struct stu b; } // 局部 -> SS, 入口 sub sp, 5 分配
全局結構體在 DS 連續佔 5 字節;局部結構體在棧幀中連續佔 5 字節,用 mov 而非 push 初始化。
struct stu func(struct stu s) { return s; }
16 位實模式下的段:偏移寻址、調用者清棧 CDECL、結構體返回值通過靜態臨時區地址傳遞,是 x86 早期 ABI 的縮影。現代 x86-64 System V ABI:
- 參數前 6 個用寄存器 (RDI, RSI, RDX, RCX, R8, R9),其餘上棧。
- 返回值:整數/指針用 RAX,浮點用 XMM0,大結構體由調用者分配空間並傳遞隱藏指針 (RDI)。
- 紅區、影子空間、栈對齊 16B 等新規則。
理解底層機制,才能寫出高性能、可移植的系統代碼,也能看懂編譯器優化後的汇編輸出。