讀程式設計師的自我修養之編譯

2021-08-06 08:32:53 字數 2659 閱讀 6062

編譯器的主要作用就是將高階語言編譯成機器語言的乙個工具;在編譯器出現之前,程式設計師都忙碌於使用機器語言或者組合語言程式設計,這是乙個複雜而又繁瑣的工作。同時,編寫的程式只能在特定的cpu環境下執行,如果換一種cpu環境,又需要重新開發,這幾乎是讓人不能接受的。到了上個世紀六七十年代,出現了很多高階程式語言,比如c++和fortran,這些高階語言的出現,使得程式設計師能夠根據專注於程式邏輯的本身,而儘量減少對計算機本身的考慮,高階語言雖然出現了,但是它不能夠直接在計算機上執行,它需要轉換成機器語言後,才能夠被計算機所識別執行,這個時候就需要編譯器了;編譯器的主要功能就是將高階語言轉成對應的機器**。

編譯器的編譯過程主要可以分為掃瞄、語法分析、語義分析、源**優化、**生成、目標**優化這6大步。現在我們就這6步進行逐個介紹。

1、掃瞄(也叫詞法分析)

首先編譯器呼叫掃瞄器,掃瞄源**,並進行詞法分析,將源**的字串行分隔成一堆記號。舉個例子:

array[index] =  (index+4)*(2+6);

compilerexpression.c

上面的那行**中包含了28個非空字元,經過掃瞄以後產生16個符號

詞法分析產生的記號一般分為如下幾類:關鍵字、識別符號、字面量(包含數字、字串等)和特殊符號(比如加號、減號等);在表示記號的同時,掃瞄器也完成了其他的工作,比如將識別符號存放到符號表,將數字存放到文字表等。常見的詞法分析程式包括lex程式等。當然,對於一些有預處理的語言,比如c/c++,不能一上來就直接對檔案進行這樣分析,還需要進行乙個預處理過程,包括消除注釋、巨集展開等等。

2、語法分析

在掃瞄器完成詞法分析過程後,接下來就輪到語法分析器工作了;語法分析器對產生的記號進行語法分析,生成對應的語法樹,整個語法分析過程,採用了上下文無關語法的分析手段。通過語法分析器生成以表示式為節點的樹,即語法樹;

如上圖,符號和數字是最小的表示式,他們不是由其他的表示式組合而成的,所以他們通常為整個語法樹的葉節點;在語法分析的同事,很多運算符號的優先順序和含義也都被確定下來了。另外,有些符號具有多重含義,比如*可以為取指標內容,也可以是乘號,在這個時候也要確定下來他們的具體含義。如同前面詞法分析器有lex,語法分析器也有一種專門的工具,叫做yacc( yet another complier complier ), 簡稱「編譯器編譯器」。

3、語義分析

語義分析由語義分析器完成。語法分析僅僅是對表示式層面的語法分析,但它不是真的了解這個語法是否真正有意義。比如c語言裡面,兩個指標做乘法運算是沒有意義的,但是在語法層面卻是合法的。編譯器所能分析的是靜態語義;所謂靜態語義是指在編譯期可以確定的語義,與之對應的是動態語義。 靜態語義通常包含申明和型別匹配、型別轉換、比如型別的隱示轉換。

經過語義轉換以後,整個語法樹的表示式都被標上了型別,如果有需要做隱式轉換的,語義分析器會在語法樹中插入相應的節點。上面的2.3圖在經過語法分析器分析後,變成如下圖所示:

4、源**優化

源**優化,也稱作為中間語言生成,現代的編譯器有著很多層次的優化,往往在源**級別就開始進行了優化。在不同的編譯器中,源**優化器有著不同的定義。源**優化器會在源**級別進行優化,比如上面例子中的(2+6)表示式會被直接優化掉,因為它的賦值直接在編譯器就可以確定,不需要等到執行期。類似的還有很多其他的複雜的優化過程,這裡就不一一陳述了。

其實,在真實的源**優化器中,直接在語法樹上進行優化比較困難,所以源**優化器往往將整個語法樹轉換成中間**;中間**,即語法樹的順序表達,其實已經非常接近目標**了,但它跟執行的目標機器已經執行環境無關,比如它不包含資料的位址,資料的尺寸等等。中間**在不同的編譯器中可能有著不同的型別,常見的型別有三位址碼和p—**;

以三位址碼為例,最基本的三位址碼是這樣的:x = y op z,這個三位址碼表示將 y 和 z 進行 op操作,然後賦值給x;上面的例子中的語法樹可以被翻譯成三位址碼後是這樣的。t1 = 2+6

t2 = index + 4

t3 = t2 * t1

array[index] = t3

可以看到,這裡為了使所有的**都符合三位址碼,我們加入了一些臨時變數t1、t2 等,在三位址碼的基礎上進行優化,將2 + 6 的結果計算出來,得到t1 = 8,然後在後面的**中t1用數字8替換,省去了乙個臨時變數。同時,t3也是可以替換的,通過重複利用t2變數,進行優化得到下面的**

t2 = index + 4

t2 = t2 * 8

中間**使得編譯器可以被分為前端和後端,編譯器前端負責產生機器無關的中間**,編譯器後端負責將中間**轉換成目標機器**。

5、目標**生成及優化

這裡直接將後面兩步目標帶生成、目標**優化歸到一塊講,他們都可以歸屬到編譯器後端。編譯器後端主要包括**生成器和目標**優化器。**生成器主要負責將中間**轉換成目標機器**,這個過程比較依賴於目標機器,因為不同的機器有著不同的字長,暫存器、整數資料型別等等,

最後目標**優化器對上面生成的目標**進行優化,比如選擇合適的定址方式,使用移位操作來代替乘法、刪除多餘的指令等。

現代的編譯器有著非常複雜的結構,這是因為高階程式語言本身非常複雜,比如c++語言本身定義就非常複雜,現在仍然沒有乙個編譯器能夠完整支援c++標準語言所規定的所有語言特性。同時,現代的計算機cpu也是非常的複雜,cpu本身採用了諸如流水線、多發射、超標量等複雜特性。為了支援這些特性,編譯器的機器指令優化過程也變得複雜。使得編譯更為複雜的是,有些編譯器可以同時支援多種不同的硬體環境,比如著名的gcc就幾乎能夠支援所有的cpu平台。

程式設計師的自我修養

一忌 輕易言敗,沒有自信 沒有永不放棄精神的程式設計師,只是乙個有程式設計師名號的假程式設計師。乙個真正的程式設計師,知道在程式設計的過程中,可能會遇到不計其數的困難和問題,可能有極多的挫折和失敗,而成功只有一次。就為解決乙個問題,我們可能連續十幾甚至幾十小時的坐在計算機前不停的工作。乙個問題解決了...

程式設計師的自我修養

一忌 輕易言敗,沒有自信 沒有永不放棄精神的程式設計師,只是乙個有程式設計師名號的假程式設計師。乙個真正的程式設計師,知道在程式設計的過程中,可能會遇到不計其數的困難和問題,可能有極多的挫折和失敗,而成功只有一次。就為解決乙個問題,我們可能連續十幾甚至幾十小時的坐在計算機前不停的工作。乙個問題解決了...

程式設計師的自我修養

對合作夥伴的尊重與包容 每個人的水平必然是不相同,大家各有擅長,並且在不斷進步中。所以遇到同事或者合作的友商犯了比較低階 對你來說可能是 的錯誤是很正常的事情,我們更多的是應該給與尊重與包容,而不是嘲笑或者消極對待。在不斷的鼓勵同事和合作夥伴之後,能感受到大家對我的信賴,特別是和友商的合作變得非常的...