組合語言與 CPU
程式計數器/指令指標
想像你讀食譜時,手指按在即將要做的那一行上。每做完一步,手指就往下滑到下一行。程式計數器就是 CPU 的手指:一個特殊暫存器,存放下一條要執行指令的位址。正是它讓機器在程式中持續前進。
基本循環是:CPU 讀取程式計數器所指位址上的指令,把程式計數器往前推到剛好越過這條指令,然後執行它。由於指令在記憶體中一條接一條排列,往前推剛好會落在下一條上——這就是程式碼預設由上而下執行的原因。在 x86-64 上這個暫存器叫 rip(指令指標);在 ARM 上叫程式計數器 pc。跳躍或分支指令的做法很單純,就是把一個新位址寫進程式計數器,讓 CPU 的手指落到別處;函式呼叫會先把當前的程式計數器存成返回位址再跳走;返回則把那個存起來的位址讀回程式計數器。
幾乎所有與流程控制有關的事,其實都只是程式計數器在改變。跳過一段程式的 if 是一個可能會、也可能不會覆寫程式計數器的條件跳躍;迴圈是往回跳到較早的位址;呼叫與返回函式則是存起來再還原它。你在 C 裡很少直接操作它,但理解它的存在能解開謎團:一串扁平的指令清單何以能分支、迴圈與呼叫子程式。
若程式計數器存著 0x401000,而那裡的指令長 4 位元組,取指後它會變成 0x401004——除非有跳躍指令把不同的位址寫進去。
程式計數器永遠指向下一條指令;分支不過是改變它所指的目標。
程式計數器是在當前指令執行完成之前就被推進的,所以它早已指向下一條指令;相對的 call 或分支是針對這個「下一條」位址而非指令本身的位址計算的——這個細節常讓初讀反組譯的人困惑。
又稱
另見