當前位置:首頁 » 編程軟體 » 覆寫規則編譯原理

覆寫規則編譯原理

發布時間: 2022-09-13 01:45:57

1. 求編譯原理的名詞解釋題

詞法分析(Lexical analysis或Scanning)和詞法分析程序(Lexical analyzer或Scanner)
詞法分析階段是編譯過程的第一個階段。這個階段的任務是從左到右一個字元一個字元地讀入源程序,即對構成源程序的字元流進行掃描然後根據構詞規則識別單詞(也稱單詞符號或符號)。詞法分析程序實現這個任務。詞法分析程序可以使用lex等工具自動生成。

語法分析(Syntax analysis或Parsing)和語法分析程序(Parser)
語法分析是編譯過程的一個邏輯階段。語法分析的任務是在詞法分析的基礎上將單詞序列組合成各類語法短語,如「程序」,「語句」,「表達式」等等.語法分析程序判斷源程序在結構上是否正確.源程序的結構由上下文無關文法描述.

語義分析(Syntax analysis)
語義分析是編譯過程的一個邏輯階段. 語義分析的任務是對結構上正確的源程序進行上下文有關性質的審查, 進行類型審查.例如一個C程序片斷:
int arr[2],b;
b = arr * 10;
源程序的結構是正確的.
語義分析將審查類型並報告錯誤:不能在表達式中使用一個數組變數,賦值語句的右端和左端的類型不匹配.

Lex
一個詞法分析程序的自動生成工具。它輸入描述構詞規則的一系列正規式,然後構建有窮自動機和這個有窮自動機的一個驅動程序,進而生成一個詞法分析程序.

Yacc
一個語法分析程序的自動生成工具。它接受語言的文法,構造一個LALR(1)分析程序.因為它採用語法制導翻譯的思想,還可以接受用C語言描述的語義動作,從而構造一個編譯程序. Yacc 是 Yet another compiler compiler的縮寫.[回頁首]

源語言(Source language)和源程序(Source program)
被編譯程序翻譯的程序稱為源程序,書寫該程序的語言稱為源語言.[回頁首]

目標語言(Object language or Target language)和目標程序(Object program or Target program)
編譯程序翻譯源程序而得到的結果程序稱為目標程序, 書寫該程序的語言稱為目標語言.[回頁首]

中間語言(中間表示)(Intermediate language(representation))
在進行了語法分析和語義分析階段的工作之後,有的編譯程序將源程序變成一種內部表示形式,這種內部表示形式叫做中間語言或中間表示或中間代碼。所謂「中間代碼」是一種結構簡單、含義明確的記號系統,這種記號系統復雜性介於源程序語言和機器語言之間,容易將它翻譯成目標代碼。另外,還可以在中間代碼一級進行與機器無關的優化。

[回頁首]

文法(Grammars)
文法是用於描述語言的語法結構的形式規則。文法G定義為四元組(,,,)。其中為非終結符號(或語法實體,或變數)集;為終結符號集;為產生式(也稱規則)的集合;產生式(規則)是形如或 a ::=b 的(a , b)有序對,其中(∪)且至少含有一個非終結符,而(∪)。,和是非空有窮集。稱作識別符號或開始符號,它是一個非終結符,至少要在一條規則中作為左部出現。
一個文法的例子: G=(={A,R},={0,1} ,={A?0R,A?01,R?A1},=A) [回頁首]
文法分類(A hierarchy of Grammars)
著名語言學家Noam Chomsky定義了四類文法和四種形式語言類,文法的四種類型分別是0型、1型、2型和3型。幾類文法的差別在於對產生式施加不同的限制,分別是:
0型文法(短語結構文法)(phrase structure grammars):
設G=(,,,),如果它的每個產生式是這樣一種結構: (∪) 且至少含有一個非終結符,而(∪),則G是一個0型文法。
1型文法(上下文有關文法)(context-sensitive grammars):
設G=(,,,)為一文法,若中的每一個產生式均滿足|,僅僅 除外,則文法G是1型或上下文有關的。
2型文法(上下文無關文法)(context-free grammars):
設G=(,,,),若P中的每一個產生式滿足:是一非終結符,(∪) 則此文法稱為2型的或上下文無關的。
3型文法(正規文法)(regular grammars):
設G=(,,,),若中的每一個產生式的形式都是A→aB或A→a,其中A和B都是非終結,a是終結符,則G是3型文法或正規文法。
0型文法產生的語言稱為0型語言。
1型文法產生的語言稱為1型語言,也稱作上下文有關語言。
2型文法產生的語言稱為2型語言,也稱作上下文無關語言。
3型文法產生的語言稱為3型語言,也稱作正規語言。

2. 為什麼覆寫equals的時候一定要覆寫hashCode

有點長,但是建議看完,因為這個問題面試經常問。

hashCode是編譯器為不同對象產生的不同整數,根據equal方法的定義:如果兩個對象是相等(equal)的,那麼兩個對象調用 hashCode必須產生相同的整數結果,即:equal為true,hashCode必須為true,equal為false,hashCode也必須 為false,所以必須重寫hashCode來保證與equal同步。

class Student { int num;
String name;

Student(int num, String name) { this.num = num; this.name = name;
} public int hashCode() { return num * name.hashCode();
} public boolean equals(Object o) {
Student s = (Student) o; return num == s.num && name.equals(s.name);
} public String toString() { return num + ":" + name;
}
}

java的集合中,判斷兩個對象是否相等的規則是:

  • 判斷兩個對象的hashCode是否相等
    如果不相等,認為兩個對象也不相等,完畢
    如果相等,轉入2

  • 判斷兩個對象用equals運算是否相等
    如果不相等,認為兩個對象也不相等
    如果相等,認為兩個對象相等

  • 1、為什麼要重載equal方法?

    答案:因為Object的equal方法默認是兩個對象的引用的比較,意思就是指向同一內存,地址則相等,否則不相等;如果你現在需要利用對象裡面的值來判斷是否相等,則重載equal方法。

    2、為什麼重載hashCode方法?

    答案:一般的地方不需要重載hashCode,只有當類需要放在HashTable、HashMap、HashSet等等hash結構的集合時才會 重載hashCode,那麼為什麼要重載hashCode呢?就HashMap來說,好比HashMap就是一個大內存塊,裡面有很多小內存塊,小內存塊 裡面是一系列的對象,可以利用hashCode來查找小內存塊hashCode%size(小內存塊數量),所以當equal相等時,hashCode必 須相等,而且如果是object對象,必須重載hashCode和equal方法。

    3、 為什麼equals()相等,hashCode就一定要相等,而hashCode相等,卻不要求equals相等?
    答案:
    - 因為是按照hashCode來訪問小內存塊,所以hashCode必須相等。
    - HashMap獲取一個對象是比較key的hashCode相等和equal為true。

    之所以hashCode相等,卻可以equal不等,就比如ObjectA和ObjectB他們都有屬性name,那麼hashCode都以name計算,所以hashCode一樣,但是兩個對象屬於不同類型,所以equal為false。

    4、 為什麼需要hashCode?

  • 通過hashCode可以很快的查到小內存塊。

  • 通過hashCode比較比equal方法快,當get時先比較hashCode,如果hashCode不同,直接返回false。

  • 以下是一個具體類的實例代碼:

  • public class Person

  • {

  • private String name; private int age; @Override

  • public int hashCode()

  • { final int prime = 31; int result = 1;

  • result = prime * result + age;

  • result = prime * result + ((name == null) ? 0 : name.hashCode()); return result;

  • } @Override

  • public boolean equals(Object obj)

  • { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false;

  • Person other = (Person) obj; if (age != other.age) return false; if (name == null)

  • { if (other.name != null) return false;

  • } else if (!name.equals(other.name)) return false; return true;

  • }

  • }2324252627282930313233343536

  • 首先equals()和hashcode()這兩個方法都是從object類中繼承過來的。
    equals()方法在object類中定義如下:

  • public boolean equals(Object obj) {

  • return (this == obj); //==永遠是比較兩個對象的地址值} 123

  • 很 明顯是對兩個對象的地址值進行的比較(即比較引用是否相同)。但是我們必需清楚,當String 、Math、還有Integer、Double。。。。等這些封裝類在使用equals()方法時,已經覆蓋了object類的equals()方法。比 如在String類中如下:

  • public boolean equals(Object anObject) { if (this == anObject) { return true;

  • } if (anObject instanceof String) {

  • String anotherString = (String)anObject; int n = count; if (n == anotherString.count) { char v1[] = value; char v2[] = anotherString.value; int i = offset; int j = anotherString.offset; while (n-- != 0) { if (v1[i++] != v2[j++]) return false;

  • } return true;

  • }

  • } return false;

  • }

  • 很明顯,這是進行的內容比較,而已經不再是地址的比較。依次類推Double、Integer、Math。。。。等等這些類都是重寫了equals()方法的,從而進行的是內容的比較。當然了基本類型是進行值的比較,這個沒有什麼好說的。

    我們還應該注意,Java語言對equals()的要求如下,這些要求是必須遵循的:

  • 對稱性:如果x.equals(y)返回是「true」,那麼y.equals(x)也應該返回是「true」。

  • 反射性:x.equals(x)必須返回是「true」。

  • 類推性:如果x.equals(y)返回是「true」,而且y.equals(z)返回是「true」,那麼z.equals(x)也應該返回是「true」。

  • 還有一致性:如果x.equals(y)返回是「true」,只要x和y內容一直不變,不管你重復x.equals(y)多少次,返回都是「true」。

  • 任何情況下,x.equals(null),永遠返回是「false」;x.equals(和x不同類型的對象)永遠返回是「false」。

  • 以上這五點是重寫equals()方法時,必須遵守的准則,如果違反會出現意想不到的結果,請大家一定要遵守。

    其次是hashcode() 方法,在object類中定義如下:

  • public native int hashCode(); 1

  • 說 明是一個本地方法,它的實現是根據本地機器相關的。當然我們可以在自己寫的類中覆蓋hashcode()方法,比如String、Integer、 Double。。。。等等這些類都是覆蓋了hashcode()方法的。例如在String類中定義的hashcode()方法如下:

  • public int hashCode() { int h = hash; if (h == 0) { int off = offset; char val[] = value; int len = count; for (int i = 0; i < len; i++) {

  • h = 31*h + val[off++];

  • }

  • hash = h;

  • } return h;

  • }1234567891011121314

  • 解釋一下這個程序(String的API中寫到):

  • s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] 1

  • 使用 int 演算法,這里 s[i] 是字元串的第 i 個字元,n 是字元串的長度,^ 表示求冪。(空字元串的哈希碼為 0。)

    首先,想要明白hashCode的作用,你必須要先知道Java中的集合。

    總的來說,Java中的集合(Collection)有兩類,一類是List,再有一類是Set。
    你知道它們的區別嗎?前者集合內的元素是有序的,元素可以重復;後者元素無序,但元素不可重復。

    那麼這里就有一個比較嚴重的問題了:要想保證元素不重復,可兩個元素是否重復應該依據什麼來判斷呢?

    這就是Object.equals方法了。但是,如果每增加一個元素就檢查一次,那麼當元素很多時,後添加到集合中的元素比較的次數就非常多了。

    也就是說,如果集合中現在已經有1000個元素,那麼第1001個元素加入集合時,它就要調用1000次equals方法。這顯然會大大降低效率。

    於是,Java採用了哈希表的原理。哈希(Hash)實際上是個人名,由於他提出一哈希演算法的概念,所以就以他的名字命名了。

    哈希演算法也稱為散列演算法,是將數據依特定演算法直接指定到一個地址上。如果詳細講解哈希演算法,那需要更多的文章篇幅,我在這里就不介紹了。

    初學者可以這樣理解,hashCode方法實際上返回的就是對象存儲的物理地址(實際可能並不是)。

    這樣一來,當集合要添加新的元素時,先調用這個元素的hashCode方法,就一下子能定位到它應該放置的物理位置上。

    如果這個位置上沒有元素,它就可以直接存儲在這個位置上,不用再進行任何比較了;如果這個位置上已經有元素了,
    就調用它的equals方法與新元素進行比較,相同的話就不存了,不相同就散列其它的地址。

    所以這里存在一個沖突解決的問題。這樣一來實際調用equals方法的次數就大大降低了,幾乎只需要一兩次。

    所以,Java對於eqauls方法和hashCode方法是這樣規定的:

  • 如果兩個對象相同,那麼它們的hashCode值一定要相同;

  • 如果兩個對象的hashCode相同,它們並不一定相同 上面說的對象相同指的是用eqauls方法比較。你當然可以不按要求去做了,但你會發現,相同的對象可以出現在Set集合中。同時,增加新元素的效率會大大下降。

  • 這里我們首先要明白一個問題:
    equals()相等的兩個對象,hashcode()一定相等;
    equals()不相等的兩個對象,卻並不能證明他們的hashcode()不相等。換句話說,equals()方法不相等的兩個對象,hashcode()有可能相等。(我的理解是由於哈希碼在生成的時候產生沖突造成的)。
    反 過來:hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。解釋下第3點的 使用范圍,我的理解是在object、String等類中都能使用。在object類中,hashcode()方法是本地方法,返回的是對象的地址值,而 object類中的equals()方法比較的也是兩個對象的地址值,如果equals()相等,說明兩個對象地址值也相等,當然hashcode()也 就相等了;在String類中,equals()返回的是兩個對象內容的比較,當兩個對象內容相等時,
    Hashcode()方法根據 String類的重寫(第2點裡面已經分析了)代碼的分析,也可知道hashcode()返回結果也會相等。以此類推,可以知道Integer、 Double等封裝類中經過重寫的equals()和hashcode()方法也同樣適合於這個原則。當然沒有經過重寫的類,在繼承了object類的 equals()和hashcode()方法後,也會遵守這個原則。

  • 談到hashcode()和equals()就不能不說到hashset,hashmap,hashtable中的使用,具體是怎樣呢,請看如下分析:
    Hashset是繼承Set介面,Set介面又實現Collection介面,這是層次關系。那麼hashset是根據什麼原理來存取對象的呢?
    在hashset中不允許出現重復對象,元素的位置也是不確定的。在hashset中又是怎樣判定元素是否重復的呢?這就是問題的關鍵所在,經過一下午的查詢求證終於獲得了一點啟示,和大家分享一下,在java的集合中,判斷兩個對象是否相等的規則是:

  • 判斷兩個對象的hashCode是否相等
    如果不相等,認為兩個對象也不相等,完畢
    如果相等,轉入2)
    (這一點只是為了提高存儲效率而要求的,其實理論上沒有也可以,但如果沒有,實際使用時效率會大大降低,所以我們這里將其做為必需的。後面會重點講到這個問題。)

  • 判斷兩個對象用equals運算是否相等
    如果不相等,認為兩個對象也不相等
    如果相等,認為兩個對象相等(equals()是判斷兩個對象是否相等的關鍵)
    為什麼是兩條准則,難道用第一條不行嗎?不行,因為前面已經說了,hashcode()相等時,equals()方法也可能不等,所以必須用第2條准則進行限制,才能保證加入的為非重復元素。

  • 比如下面的代碼:

  • public static void main(String args[]) {

  • String s1 = new String("zhaoxudong");

  • String s2 = new String("zhaoxudong");

  • System.out.println(s1 == s2);//false

  • System.out.println(s1.equals(s2));//true

  • System.out.println(s1.hashCode());//s1.hashcode()等於s2.hashcode()

  • System.out.println(s2.hashCode());

  • Set hashset = new HashSet();

  • hashset.add(s1);

  • hashset.add(s2);

  • }1234567891011

  • 實質上在添加s1,s2時,運用上面說到的兩點准則,可以知道hashset認為s1和s2是相等的,是在添加重復元素,所以讓s2覆蓋了s1;

  • Iterator it=hashset.iterator(); while(it.hasNext())

  • {

  • System.out.println(it.next()); } 12345

  • 最後在while循環的時候只列印出了一個」zhaoxudong」。
    輸出結果為:

  • false

  • true

  • -967303459 -967303459 1234

  • 這是因為String類已經重寫了equals()方法和hashcode()方法,所以在根據上面的第1.2條原則判定時,hashset認為它們是相等的對象,進行了重復添加。
    但是看下面的程序:

  • import java.util.*;public class HashSetTest

  • { public static void main(String[] args)

  • {

  • HashSet hs=new HashSet();

  • hs.add(new Student(1,"zhangsan"));

  • hs.add(new Student(2,"lisi"));

  • hs.add(new Student(3,"wangwu"));

  • hs.add(new Student(1,"zhangsan"));


  • Iterator it=hs.iterator(); while(it.hasNext())

  • {

  • System.out.println(it.next());

  • }

  • }

  • }

  • class Student

  • { int num;

  • String name;

  • Student(int num,String name)

  • { this.num=num; this.name=name;

  • } public String toString()

  • { return num+":"+name;

  • }

  • } 23242526272829303132

  • 輸出結果為:

  • 1:zhangsan 1:zhangsan 3:wangwu 2:lisi 1234

  • 問題出現了,為什麼hashset添加了相等的元素呢,這是不是和hashset的原則違背了呢?回答是:沒有。

    因 為在根據hashcode()對兩次建立的new Student(1,」zhangsan」)對象進行比較時,生成的是不同的哈希碼值,所以hashset把他當作不同的對象對待了,當然此時的 equals()方法返回的值也不等(這個不用解釋了吧)。那麼為什麼會生成不同的哈希碼值呢?上面我們在比較s1和s2的時候不是生成了同樣的哈希碼 嗎?原因就在於我們自己寫的Student類並沒有重新自己的hashcode()和equals()方法,所以在比較時,是繼承的object類中的 hashcode()方法,呵呵,各位還記得object類中的hashcode()方法比較的是什麼吧!!

    它是一個本地方法,比較的是對象 的地址(引用地址),使用new方法創建對象,兩次生成的當然是不同的對象了(這個大家都能理解吧。。。),造成的結果就是兩個對象的 hashcode()返回的值不一樣。所以根據第一個准則,hashset會把它們當作不同的對象對待,自然也用不著第二個准則進行判定了。那麼怎麼解決 這個問題呢??
    答案是:在Student類中重新hashcode()和equals()方法。
    例如:

  • class Student

  • { int num;

  • String name;

  • Student(int num,String name)

  • { this.num=num; this.name=name;

  • } public int hashCode()

  • { return num*name.hashCode();

  • } public boolean equals(Object o)

  • {

  • Student s=(Student)o; return num==s.num && name.equals(s.name);

  • } public String toString()

  • { return num+":"+name;

  • }

  • } 23

  • 根據重寫的方法,即便兩次調用了new Student(1,」zhangsan」),我們在獲得對象的哈希碼時,根據重寫的方法hashcode(),獲得的哈希碼肯定是一樣的(這一點應該沒有疑問吧)。

    當然根據equals()方法我們也可判斷是相同的。所以在向hashset集合中添加時把它們當作重復元素看待了。所以運行修改後的程序時,我們會發現運行結果是:

  • 1:zhangsan 3:wangwu 2:lisi 123

  • 可以看到重復元素的問題已經消除。

    關於在hibernate的pojo類中,重新equals()和hashcode()的問題:
    1),重點是equals,重寫hashCode只是技術要求(為了提高效率)
    2),為什麼要重寫equals呢,因為在java的集合框架中,是通過equals來判斷兩個對象是否相等的
    3),在hibernate中,經常使用set集合來保存相關對象,而set集合是不允許重復的。

    我們再來談談前面提到在向hashset集合中添加元素時,怎樣判斷對象是否相同的准則,前面說了兩條,其實只要重寫equals()這一條也可以。

    但 當hashset中元素比較多時,或者是重寫的equals()方法比較復雜時,我們只用equals()方法進行比較判斷,效率也會非常低,所以引入了 hashcode()這個方法,只是為了提高效率,但是我覺得這是非常有必要的(所以我們在前面以兩條准則來進行hashset的元素是否重復的判斷)。
    比如可以這樣寫:

  • public int hashCode(){

  • return 1;}//等價於hashcode無效12

  • 這樣做的效果就是在比較哈希碼的時候不能進行判斷,因為每個對象返回的哈希碼都是1,每次都必須要經過比較equals()方法後才能進行判斷是否重復,這當然會引起效率的大大降低。

3. 編譯原理的文法是什麼

文法是描述語言規則的形式規則。實際上就是用一個四元組G=(VT,VN,S,P)定義的一個推理方式。其中VT是終結符,VN是非終結符,S是開始符號,P是一組產生規則。

4. 編譯原理全部的名詞解釋

書上有別那麼懶!。。。。
編譯過程的六個階段:詞法分析,語法分析,語義分析,中間代碼生成,代碼優化,目標代碼生成
解釋程序:把某種語言的源程序轉換成等價的另一種語言程序——目標語言程序,然後再執行目標程序。解釋方式是接受某高級語言的一個語句輸入,進行解釋並控制計算機執行,馬上得到這句的執行結果,然後再接受下一句。
編譯程序:就是指這樣一種程序,通過它能夠將用高級語言編寫的源程序轉換成與之在邏輯上等價的低級語言形式的目標程序(機器語言程序或匯編語言程序)。
解釋程序和編譯程序的根本區別:是否生成目標代碼
句子的二義性(這里的二義性是指語法結構上的。):文法G[S]的一個句子如果能找到兩種不同的最左推導(或最右推導),或者存在兩棵不同的語法樹,則稱這個句子是二義性的。
文法的二義性:一個文法如果包含二義性的句子,則這個文法是二義文法,否則是無二義文法。
LL(1)的含義:(LL(1)文法是無二義的; LL(1)文法不含左遞歸)
第1個L:從左到右掃描輸入串 第2個L:生成的是最左推導
1 :向右看1個輸入符號便可決定選擇哪個產生式
某些非LL(1)文法到LL(1)文法的等價變換: 1. 提取公因子 2. 消除左遞歸
文法符號的屬性:單詞的含義,即與文法符號相關的一些信息。如,類型、值、存儲地址等。
一個屬性文法(attribute grammar)是一個三元組A=(G, V, F)
G:上下文無關文法。
V:屬性的有窮集。每個屬性與文法的一個終結符或非終結符相連。屬性與變數一樣,可以進行計算和傳遞。
F:關於屬性的斷言或謂詞(一組屬性的計算規則)的有窮集。斷言或語義規則與一個產生式相聯,只引用該產生式左端或右端的終結符或非終結符相聯的屬性。
綜合屬性:若產生式左部的單非終結符A的屬性值由右部各非終結符的屬性值決定,則A的屬性稱為綜合屬
繼承屬性:若產生式右部符號B的屬性值是根據左部非終結符的屬性值或者右部其它符號的屬性值決定的,則B的屬性為繼承屬性。
(1)非終結符既可有綜合屬性也可有繼承屬性,但文法開始符號沒有繼承屬性。
(2) 終結符只有綜合屬性,沒有繼承屬性,它們由詞法程序提供。
在計算時: 綜合屬性沿屬性語法樹向上傳遞;繼承屬性沿屬性語法樹向下傳遞。
語法制導翻譯:是指在語法分析過程中,完成附加在所使用的產生式上的語義規則描述的動作。
語法制導翻譯實現:對單詞符號串進行語法分析,構造語法分析樹,然後根據需要構造屬性依賴圖,遍歷語法樹並在語法樹的各結點處按語義規則進行計算。
中間代碼(中間語言)
1、是復雜性介於源程序語言和機器語言的一種表示形式。
2、一般,快速編譯程序直接生成目標代碼。
3、為了使編譯程序結構在邏輯上更為簡單明確,常採用中間代碼,這樣可以將與機器相關的某些實現細節置於代碼生成階段仔細處理,並且可以在中間代碼一級進行優化工作,使得代碼優化比較容易實現。
何謂中間代碼:源程序的一種內部表示,不依賴目標機的結構,易於代碼的機械生成。
為何要轉換成中間代碼:(1)邏輯結構清楚;利於不同目標機上實現同一種語言。
(2)便於移植,便於修改,便於進行與機器無關的優化。
中間代碼的幾種形式:逆波蘭記號 ,三元式和樹形表示 ,四元式
符號表的一般形式:一張符號表的的組成包括兩項,即名字欄和信息欄。
信息欄包含許多子欄和標志位,用來記錄相應名字和種種不同屬性,名字欄也稱主欄。主欄的內容稱為關鍵字(key word)。
符號表的功能:(1)收集符號屬性 (2) 上下文語義的合法性檢查的依據: 檢查標識符屬性在上下文中的一致性和合法性。(3)作為目標代碼生成階段地址分配的依據
符號的主要屬性及作用:
1. 符號名 2. 符號的類型 (整型、實型、字元串型等))3. 符號的存儲類別(公共、私有)
4. 符號的作用域及可視性 (全局、局部) 5. 符號變數的存儲分配信息 (靜態存儲區、動態存儲區)
存儲分配方案策略:靜態存儲分配;動態存儲分配:棧式、 堆式。
靜態存儲分配
1、基本策略
在編譯時就安排好目標程序運行時的全部數據空間,並能確定每個數據項的單元地址。
2、適用的分配對象:子程序的目標代碼段;全局數據目標(全局變數)
3、靜態存儲分配的要求:不允許遞歸調用,不含有可變數組。
FORTRAN程序是段結構,不允許遞歸,數據名大小、性質固定。 是典型的靜態分配
動態存儲分配
1、如果一個程序設計語言允許遞歸過程、可變數組或允許用戶自由申請和釋放空間,那麼,就需要採用動態存儲管理技術。
2、兩種動態存儲分配方式:棧式,堆式
棧式動態存儲分配
分配策略:將整個程序的數據空間設計為一個棧。
【例】在具有遞歸結構的語言程序中,每當調用一個過程時,它所需的數據空間就分配在棧頂,每當過程工作結束時就釋放這部分空間。
過程所需的數據空間包括兩部分
一部分是生存期在本過程這次活動中的數據對象。如局部變數、參數單元、臨時變數等;
另一部分則是用以管理過程活動的記錄信息(連接數據)。
活動記錄(AR)
一個過程的一次執行所需要的信息使用一個連續的存儲區來管理,這個區 (塊)叫做一個活動記錄。
構成
1、臨時工作單元;2、局部變數;3、機器狀態信息;4、存取鏈;
5、控制鏈;6、實參;7、返回地址
什麼是代碼優化
所謂優化,就是對代碼進行等價變換,使得變換後的代碼運行結果與變換前代碼運行結果相同,而運行速度加快或佔用存儲空間減少。
優化原則:等價原則:經過優化後不應改變程序運行的結果。
有效原則:使優化後所產生的目標代碼運行時間較短,佔用的存儲空間較小。
合算原則:以盡可能低的代價取得較好的優化效果。
常見的優化技術
(1) 刪除多餘運算(刪除公共子表達式) (2) 代碼外提 +刪除歸納變數+ (3)強度削弱; (4)變換循環控制條件 (5)合並已知量與復寫傳播 (6)刪除無用賦值
基本塊定義
程序中只有一個入口和一個出口的一段順序執行的語句序列,稱為程序的一個基本塊。

給我分數啊。。。

5. 編譯原理問題:求解

E是文法開頭。ε代表終結符號(推理中代表終點或結果,程序語言中代表常量等)。E T 這些大寫字母一般代表非終結符號(這些代表中間過程,非結果。程序中代表函數等等)。開始是E。因為有個G(E)。E就是文法開始符號。推導就有E開始,它也是一個非終結符(代表函數、或者一個推導過程,類似於程序中的main(c++)、winmain(vc++)、dllmain(dll)等主函數)。

1算術表達式文法:這個文法是一個遞歸文法。計算機進行邏輯推導時會走很多彎路(類似於遍歷一顆樹的過程)。為了不讓計算機走彎路(提高效率的目的),可以變換為第二種文法。這種文法消除了遞歸(消除了歧義,類似於後綴表達式),使計算機可以一條直線走到底兒推導出結果。

我也很久沒看編譯原理了。 呵呵

6. 誰能夠解釋下編譯原理中什麼是FIRSTVT,和LASTVT,盡量淺顯易懂點謝謝

給你COPY一個看管用不,雖然不懂你在問什麼...

算符優先分析 [上一節] [下一節]

5.2.1 算符優先文法及其優先表構造

一個文法,如果它的任一產生式的右部都不含兩個相繼(並列)的非終結符,即不含如下形式的產生式右部:

…QR…

則我們稱該文法為算符文法。

在後面的定義中,a、b代表任意終結符;P、Q、R代表任意非終結符;『…』代表由終結符和非終結符組成的任意序列,包括空字。

假定G是一個不含e-產生式的算符文法。對於任何一對終結符a、b,我們說:

1. a�6�7b當且僅當文法G中含有形如P→…ab…或P→…aQb…的產生式;

2. a�6�3b當且僅當G中含有形如P→…aR…的產生式,而Rb…或RQb…;

3. a�6�4b當且僅當G中含有形如P→…Rb…的產生式,而R…a或R…aQ。

如果一個算符文法G中的任何終結符對(a,b)至多隻滿足下述三關系之一:

a�6�7b,a�6�3b, a�6�4b

則稱G是一個算符優先文法。

現在來研究從算符優先文法G構造優先關系表的演算法。

通過檢查G的每個產生式的每個候選式,可找出所有滿足a�6�7b的終結符對。為了找出所有滿足關系�6�3和�6�4的終結符對,我們首先需要對G的每個非終結符P構造兩個集合FIRSTVT(P)和LASTVT(P):

FIRSTVT(P)={a | Pa…或PQa…,a�0�2VT而Q�0�2VN}

LASTVT(P)={a | P…a或P…aQ,a�0�2VT而Q�0�2VN}

5.2.2 算符優先分析演算法

所謂素短語是指這樣的一個短語,它至少含有一個終結符,並且,除它自身之外不再含任何更小的素短語。所謂最左素短語是指處於句型最左邊的那個素短語。如上例,P*P和i是句型P*P+i的素短語,而P*P是它的最左素短語。

現在考慮算符優先文法,我們把句型(括在兩個#之間)的一般形式寫成:

#N1a1N2a2…NnanNn+1# (5.4)

其中,每個ai都是終結符,Ni是可有可無的非終結符。換言之,句型中含有n個終結符,任何兩個終結符之間頂多隻有一個非終結符。必須記住,任何算符文法的句型都具有這種形式。我們可以證明如下定理(證明留給有興趣的讀者作練習):

一個算符優先文法G的任何句型(5.4)的最左素短語是滿足如下條件的最左子串Njaj…NiaiNi+1,

aj-1�6�3aj

aj�6�7 aj+1,…,ai-1�6�7ai

ai�6�4ai+1

根據這個定理,下面我們討論算符優先分析演算法。為了和定理的敘述相適應,我們現在僅使用一個符號棧S,既用它寄存終結符,也用它寄存非終結符。下面的分析演算法是直接根據這個定理構造出來的,其中k代表符號棧S的使用深度。

5.2.3 優先函數

在實際實現算符優先分析演算法時,一般不用表5.1這樣的優先表,而是用兩個優先函數f和g。我們把每個終結符q與兩個自然數f(q)和g(q)相對應,使得

若q1�6�3q2 則 f(q1)<g(q2)

若q1�6�7q2 則 f(q1)= g(q2) (5.5)

若q1�6�4q2 則 f(q1)>g(q2)

函數f稱為入棧優先函數,g稱為比較優先函數。使用優先函數有兩方面的優點:便於作比較運算,並且節省存儲空間,因為優先關系表佔用的存儲量比較大。其缺點是,原先不存在優先關系的兩個終結符,由於與自然數相對應,變成可比較的了。因而,可能會掩蓋輸入串的某些錯誤。但是,我們可以通過檢查棧頂符號q和輸入符號a的具體內容來發現那些原先不可比較的情形。

如果優先函數存在,那麼,從優先表構造優先函數的一個簡單方法是:

1. 對於每個終結符a(包括#)令其對應兩個符號fa和ga,畫一張以所有符號fa和ga為結點的方向圖,如果a �6�4�6�7b,那麼,就從fa畫一箭弧至gb;如果a�6�3�6�7b,就畫一條從gb到fa的箭弧。

7. JAVA中覆寫和重載有什麼區別啊謝謝~

(1)方法重載是讓類以統一的方式處理不同類型數據的一種手段。多個同名函數同時存在,具有不同的參數個數/類型。重載Overloading是一個類中多態性的一種表現。

(2)Java的方法重載,就是在類中可以創建多個方法,它們具有相同的名字,但具有不同的參數和不同的定義。調用方法時通過傳遞給它們的不同參數個數和參數類型來決定具體使用哪個方法, 這就是多態性。

(3)重載的時候,方法名要一樣,但是參數類型和個數不一樣,返回值類型可以相同也可以不相同。無法以返回型別作為重載函數的區分標准。

下面是重載的例子:
package c04.answer;//這是包名
//這是這個程序的第一種編程方法,在main方法中先創建一個Dog類實例,然後在Dog類的構造方法中利用this關鍵字調用不同的bark方法。不同的重載方法bark是根據其參數類型的不同而區分的。

//注意:除構造器以外,編譯器禁止在其他任何地方中調用構造器。
package c04.answer;

public class Dog {
Dog()
{
this.bark();
}
void bark()//bark()方法是重載方法
{
System.out.println("no barking!");
this.bark("female", 3.4);
}
void bark(String m,double l)//注意:重載的方法的返回值都是一樣的,
{
System.out.println("a barking dog!");
this.bark(5, "China");
}
void bark(int a,String n)//不能以返回值區分重載方法,而只能以「參數類型」和「類名」來區分
{
System.out.println("a howling dog");
}

public static void main(String[] args)
{
Dog dog = new Dog();
//dog.bark();
//dog.bark("male", "yellow");
//dog.bark(5, "China");

然後我們再來談談 重寫(Overriding)
(1) 父類與子類之間的多態性,對父類的函數進行重新定義。如果在子類中定義某方法與其父類有相同的名稱和參數,我們說該方法被重寫 (Overriding)。在Java中,子類可繼承父類中的方法,而不需要重新編寫相同的方法。但有時子類並不想原封不動地繼承父類的方法,而是想作一定的修改,這就需要採用方法的重寫。方法重寫又稱方法覆蓋。

(2) 若子類中的方法與父類中的某一方法具有相同的方法名、返回類型和參數表,則新方法將覆蓋原有的方法。如需父類中原有的方法,可使用super關鍵字,該關鍵字引用了當前類的父類。

(3) 子類函數的訪問修飾許可權不能少於父類的;下面是重寫的例子:

概念:即調用對象方法的機制。

動態綁定的內幕:

1、編譯器檢查對象聲明的類型和方法名,從而獲取所有候選方法。試著把上例Base類的test注釋掉,這時再編譯就無法通過。

2、重載決策:編譯器檢查方法調用的參數類型,從上述候選方法選出唯一的那一個(其間會有隱含類型轉化)。如果編譯器找到多於一個或者沒找到,此時編譯器就會報錯。試著把上例Base類的test(byte b)注釋掉,這時運行結果是1 1。

3、若方法類型為priavte static final ,java採用靜態編譯,編譯器會准確知道該調用哪個方法。

4、當程序運行並且使用動態綁定來調用一個方法時,那麼虛擬機必須調用對象的實際類型相匹配的方法版本。在例子中,b所指向的實際類型是TestOverriding,所以b.test(0)調用子類的test。但是,子類並沒有重寫test(byte b),所以b.test((byte)0)調用的是父類的test(byte b)。如果把父類的(byte b)注釋掉,則通過第二步隱含類型轉化為int,最終調用的是子類的test(int i)。

學習總結:多態性是面向對象編程的一種特性,和方法無關。

簡單說,就是同樣的一個方法能夠根據輸入數據的不同,做出不同的處理,即方法的重載——有不同的參數列表(靜態多態性)

而當子類繼承自父類的相同方法,輸入數據一樣,但要做出有別於父類的響應時,你就要覆蓋父類方法,即在子類中重寫該方法——相同參數,不同實現(動態多態性)

OOP三大特性:繼承,多態,封裝。
public class Base
{
void test(int i)
{
System.out.print(i);
}
void test(byte b)
{
System.out.print(b);
}
}
public class TestOverriding extends Base
{
void test(int i)
{
i++;
System.out.println(i);
}
public static void main(String[]agrs)
{
Base b=new TestOverriding();
b.test(0)
b.test((byte)0)
}
}
這時的輸出結果是1 0,這是運行時動態綁定的結果。動態綁定

8. 編譯原理學了有什麼用

對大多數人來說,學過編譯原理,應該可以知道對於很多代碼的優化,編譯器其實可以做好,不需要自己寫代碼的時候杞人憂天。在通用、局部的優化上,甚至編譯器往往做得比程序員好。

大概率會意識到編譯原理背後的故事,也許會沉迷在某個方向,也許還會樂於看一些奇妙的parser構建方式。

大概還可能會去學習類型系統,發現形式化的故事似乎在很多方面都有對應的版本,而後,他們也許會嘗試走向研究,去挑戰目前都沒有好好解決的代碼優化問題,也許會走向應用,用起LLVM,在上面加個target,支持一些新硬體,做個新語言的前端等。

編譯原理是計算機專業的一門重要專業課,旨在介紹編譯程序構造的一般原理和基本方法。內容包括語言和文法、詞法分析、語法分析、語法制導翻譯、中間代碼生成、存儲管理、代碼優化和目標代碼生成。 編譯原理是計算機專業設置的一門重要的專業課程。

編譯原理課程是計算機相關專業學生的必修課程和高等學校培養計算機專業人才的基礎及核心課程,同時也是計算機專業課程中最難及最挑戰學習能力的課程之一。編譯原理課程內容主要是原理性質,高度抽象。

編譯可以分為五個基本步驟:詞法分析、語法分析、語義分析及中間代碼的生成、優化、目標代碼的生成。這是每個編譯器都必須的基本步驟和流程, 從源頭輸入高級語言源程序輸出目標語言代碼。

1、詞法分析

詞法分析器是通過詞法分析程序對構成源程序的字元串從左到右的掃描, 逐個字元地讀, 識別出每個單詞符號, 識別出的符號一般以二元式形式輸出, 即包含符號種類的編碼和該符號的值。

詞法分析器一般以函數的形式存在, 供語法分析器調用。當然也可以一個獨立的詞法分析器程序存在。完成詞法分析任務的程序稱為詞法分析程序或詞法分析器或掃描器。

2、語法分析

語法分析是編譯過程的第二個階段。這階段的任務是在詞法分析的基礎上將識別出的單詞符號序列組合成各類語法短語, 如「語句」, 「表達式」等.語法分析程序的主要步驟是判斷源程序語句是否符合定義的語法規則, 在語法結構上是否正確。

而一個語法規則又稱為文法, 喬姆斯基將文法根據施加不同的限制分為0型、1型、2型、3型文法, 0型文法又稱短語文法, 1型稱為上下文有關文法, 2型稱為上下文無關文法, 3型文法稱為正規文法, 限制條件依次遞增。

3、語義分析

詞法分析注重的是每個單詞是否合法, 以及這個單詞屬於語言中的哪些部分。語法分析的上下文無關文法注重的是輸入語句是否可以依據文法匹配產生式。

那麼, 語義分析就是要了解各個語法單位之間的關系是否合法。實際應用中就是對結構上正確的源程序進行上下文有關性質的審查, 進行類型審查等。

4、中間代碼生成與優化

在進行了語法分析和語義分析階段的工作之後, 有的編譯程序將源程序變成一種內部表示形式, 這種內部表示形式叫做中間語言或中間表示或中間代碼。

所謂「中間代碼」是一種結構簡單、含義明確的記號系統, 這種記號系統復雜性介於源程序語言和機器語言之間, 容易將它翻譯成目標代碼。另外, 還可以在中間代碼一級進行與機器無關的優化。

5、目標代碼的生成

根據優化後的中間代碼, 可生成有效的目標代碼。而通常編譯器將其翻譯為匯編代碼, 此時還需要將匯編代碼經匯編器匯編為目標機器的機器語言。

6、出錯處理

編譯的各個階段都有可能發現源碼中的錯誤, 尤其是語法分析階段可能會發現大量的錯誤, 因此編譯器需要做出錯處理, 報告錯誤類型及錯誤位置等信息。

9. C語言編譯原理是什麼

編譯共分為四個階段:預處理階段、編譯階段、匯編階段、鏈接階段。

1、預處理階段:

主要工作是將頭文件插入到所寫的代碼中,生成擴展名為「.i」的文件替換原來的擴展名為「.c」的文件,但是原來的文件仍然保留,只是執行過程中的實際文件發生了改變。(這里所說的替換並不是指原來的文件被刪除)

2、匯編階段:

插入匯編語言程序,將代碼翻譯成匯編語言。編譯器首先要檢查代碼的規范性、是否有語法錯誤等,以確定代碼的實際要做的工作,在檢查無誤後,編譯器把代碼翻譯成匯編語言,同時將擴展名為「.i」的文件翻譯成擴展名為「.s」的文件。

3、編譯階段:

將匯編語言翻譯成機器語言指令,並將指令打包封存成可重定位目標程序的格式,將擴展名為「.s」的文件翻譯成擴展名為「.o」的二進制文件。

4、鏈接階段:

在示例代碼中,改代碼文件調用了標准庫中printf函數。而printf函數的實際存儲位置是一個單獨編譯的目標文件(編譯的結果也是擴展名為「.o」的文件),所以此時主函數調用的時候,需要將該文件(即printf函數所在的編譯文件)與hello world文件整合到一起,此時鏈接器就可以大顯神通了,將兩個文件合並後生成一個可執行目標文件。

熱點內容
日本免費雲伺服器色 發布:2025-04-05 04:58:52 瀏覽:864
linuxcpp 發布:2025-04-05 04:53:38 瀏覽:747
安卓字體哪個最好 發布:2025-04-05 04:46:37 瀏覽:649
什麼是hdb3碼編解碼 發布:2025-04-05 04:40:20 瀏覽:504
編譯原理運算符 發布:2025-04-05 04:37:50 瀏覽:520
如何用安卓手機玩ipad的賬號 發布:2025-04-05 04:17:42 瀏覽:935
vivo手機怎麼在桌面建文件夾 發布:2025-04-05 04:15:56 瀏覽:961
在線ftp網頁版軟體 發布:2025-04-05 04:15:02 瀏覽:624
android手機gps 發布:2025-04-05 04:14:59 瀏覽:446
頁數演算法 發布:2025-04-05 03:19:01 瀏覽:318