許多組語開發者習慣使用副程式(PROC)而非巨集,擔心巨集會導致程式碼膨脹。然而,巨集在編譯期展開,能產生極精簡的機器碼,甚至優於手寫的資料段定義。本文透過實例剖析 MASM 巨集 CTEXT,展示其如何將字串常數自動置於 .const 區段,並產生與手寫版本完全相同的執行檔。
CTEXT MACRO y:VARARG
LOCAL sym
CONST segment
ifidni ,<>
sym db 0
else
sym db y,0
endif
CONST ends
exitm
ENDM
.code
Start:
invoke MessageBoxA, NULL, CTEXT("Hello, world !"), CTEXT("Hi!"), MB_ICONINFORMATION
invoke ExitProcess, NULL
end Start
使用 ml /c /coff /I include /EP 20-1.asm > L.txt 可查看巨集展開後的中間碼:
.code
Start:
CONST segment
??0019 db "Hello, world !",0
CONST ends
CONST segment
??001A db "Hi!",0
CONST ends
invoke MessageBoxA, NULL, offset ??0019, offset ??001A, MB_ICONINFORMATION
invoke ExitProcess, NULL
end Start
巨集自動產生唯一標籤(??0019、??001A),並將字串置於 CONST 區段,最後以 offset 取得位址傳遞給 API。
手寫版本(20-2.asm)在 .const 區段定義 xx、yy 並使用 addr 取址。對比兩個編譯後的 EXE(fc /b),僅差異於 TimeDateStamp(編譯時間戳),機器碼完全一致。
若在 .code 區段直接定義字串(20-3.asm),字串會被置於 .data 區段,導致 PE 檔多出一個區段,檔案體積變大。巨集 CTEXT 確保字串進入唯讀區段(.rdata),不僅節省空間,也符合 Windows 記憶體管理最佳實務(共享唯讀頁)。