用C/C++語言這類高階程式語言所編寫的程式原始碼是利用一種叫做“編譯原理”的技術,經過一些列的處理步驟,最終轉變為彙編指令,再最後翻譯機器指令。我們知道計算機只能處理和識別二進位制指令,而我們所編寫的程式包含各種較複雜的結構,例如 if語句、迴圈語句、繼承、多型、虛擬函式等。其實,在很早的時候,計算機科學家們就已經在研究如何把接近人類語言的高階語言所編寫的程式轉換成機器指令了。這些研究成果都歸屬於“編譯原理”領域,並且“編譯原理”是計算機專業學生的必修課。
那麼根據編譯原理,C語言是怎麼轉換成組合語言,總共分以下幾個步驟:
1. 預處理 -> 2.詞法分析 -> 3.語法分析 -> 4.語義分析 -> 5.最佳化 -> 6.連結
注意,我上面列出的過程是目前實際中真正採用的步驟,編譯原理課程中可能沒有把完整步驟列出來,只列舉了核心的幾個步驟。
上面每個步驟在編譯原理課程中都有一個專門的章節來講述。這裡大概說一下每個步驟的作用吧。
1. 預處理:負責執行C語言中的#include, #if, #else 等預處理指令。注意,這裡是去執行這些預處理指令。這些預處理指令的作用是根據你的系統環境配湊出最終版的原始碼。
2. 詞法分析:把你定義的函式名、變數名、預留的關鍵字等抽象化,用一個符號來代替,方便編譯程式處理。例如上圖中的main, return, printf等單詞,都被看作一個符號,轉換成M, R, P。在這個過程中,會檢查你的變數名、函式名名稱是否正確。
3. 語法分析:經過詞法分析處理之後,程式程式碼已經變成一堆符號了,例如 I S T F ... M I R P(放心,人已經不認識了,但是計算機能認識)。這時的符號是打散的,語法分析負責把這些符號按照一定的結構組織起來,形成一個抽象語法樹(這個結構跟你寫的程式程式碼的結構是對應起來的)。
4. 語義分析:當構造出這樣一個樹的結構之後,編譯就就會檢查語法是否正確,並且去掃描這棵樹。根據這棵樹的結構,生成中間指令了。這個中間指令已經非常接近彙編。中間指令跟彙編還是有區別的,因為不同廠家的CPU指令有所不同,所以還要根據不同廠家的CPU指令集,把這個中間指令轉換成彙編。
5. 最佳化:因為程式設計師有時程式碼寫的不太好,會導致一些多餘的操作,或者效率低的指令。最佳化過程可以找出這些毛病,自動替換成更好的指令。
6. 連結:以上過程只編譯了一個模組,一個大型程式往往包好多個模組。最後的連結過程負責把所有模組組裝起來,構造出最後可以執行的程式。
用C/C++語言這類高階程式語言所編寫的程式原始碼是利用一種叫做“編譯原理”的技術,經過一些列的處理步驟,最終轉變為彙編指令,再最後翻譯機器指令。我們知道計算機只能處理和識別二進位制指令,而我們所編寫的程式包含各種較複雜的結構,例如 if語句、迴圈語句、繼承、多型、虛擬函式等。其實,在很早的時候,計算機科學家們就已經在研究如何把接近人類語言的高階語言所編寫的程式轉換成機器指令了。這些研究成果都歸屬於“編譯原理”領域,並且“編譯原理”是計算機專業學生的必修課。
那麼根據編譯原理,C語言是怎麼轉換成組合語言,總共分以下幾個步驟:
1. 預處理 -> 2.詞法分析 -> 3.語法分析 -> 4.語義分析 -> 5.最佳化 -> 6.連結
注意,我上面列出的過程是目前實際中真正採用的步驟,編譯原理課程中可能沒有把完整步驟列出來,只列舉了核心的幾個步驟。
上面每個步驟在編譯原理課程中都有一個專門的章節來講述。這裡大概說一下每個步驟的作用吧。
1. 預處理:負責執行C語言中的#include, #if, #else 等預處理指令。注意,這裡是去執行這些預處理指令。這些預處理指令的作用是根據你的系統環境配湊出最終版的原始碼。
2. 詞法分析:把你定義的函式名、變數名、預留的關鍵字等抽象化,用一個符號來代替,方便編譯程式處理。例如上圖中的main, return, printf等單詞,都被看作一個符號,轉換成M, R, P。在這個過程中,會檢查你的變數名、函式名名稱是否正確。
3. 語法分析:經過詞法分析處理之後,程式程式碼已經變成一堆符號了,例如 I S T F ... M I R P(放心,人已經不認識了,但是計算機能認識)。這時的符號是打散的,語法分析負責把這些符號按照一定的結構組織起來,形成一個抽象語法樹(這個結構跟你寫的程式程式碼的結構是對應起來的)。
4. 語義分析:當構造出這樣一個樹的結構之後,編譯就就會檢查語法是否正確,並且去掃描這棵樹。根據這棵樹的結構,生成中間指令了。這個中間指令已經非常接近彙編。中間指令跟彙編還是有區別的,因為不同廠家的CPU指令有所不同,所以還要根據不同廠家的CPU指令集,把這個中間指令轉換成彙編。
5. 最佳化:因為程式設計師有時程式碼寫的不太好,會導致一些多餘的操作,或者效率低的指令。最佳化過程可以找出這些毛病,自動替換成更好的指令。
6. 連結:以上過程只編譯了一個模組,一個大型程式往往包好多個模組。最後的連結過程負責把所有模組組裝起來,構造出最後可以執行的程式。