導航:首頁 > 源碼編譯 > 編譯原理pascal棧式活動記錄

編譯原理pascal棧式活動記錄

發布時間:2023-03-06 10:20:02

編譯原理全部的名詞解釋

書上有別那麼懶!.
編譯過程的六個階段:詞法分析,語法分析,語義分析,中間代碼生成,代碼優化,目標代碼生成
解釋程序:把某種語言的源程序轉換成等價的另一種語言程序——目標語言程序,然後再執行目標程序.解釋方式是接受某高級語言的一個語句輸入,進行解釋並控制計算機執行,馬上得到這句的執行結果,然後再接受下一句.
編譯程序:就是指這樣一種程序,通過它能夠將用高級語言編寫的源程序轉換成與之在邏輯上等價的低級語言形式的目標程序(機器語言程序或匯編語言程序).
解釋程序和編譯程序的根本區別:是否生成目標代碼
句子的二義性(這里的二義性是指語法結構上的.):文法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)刪除無用賦值
基本塊定義
程序中只有一個入口和一個出口的一段順序執行的語句序列,稱為程序的一個基本塊.
給我分數啊.

❷ 編譯原理課程設計-詞法分析器設計(C語言)

#include"stdio.h"/*定義I/O庫所用的某些宏和變數*/

#include"string.h"/*定義字元串庫函數*/

#include"conio.h"/*提供有關屏幕窗口操作函數*/

#include"ctype.h"/*分類函數*/

charprog[80]={''},

token[8];/*存放構成單詞符號的字元串*/

charch;

intsyn,/*存放單詞字元的種別碼*/

n,

sum,/*存放整數型單詞*/

m,p;/*p是緩沖區prog的指針,m是token的指針*/

char*rwtab[6]={"begin","if","then","while","do","end"};

voidscaner(){

m=0;

sum=0;

for(n=0;n<8;n++)

token[n]='';

ch=prog[p++];

while(ch=='')

ch=prog[p++];

if(isalpha(ch))/*ch為字母字元*/{

while(isalpha(ch)||isdigit(ch))/*ch為字母字元或者數字字元*/{

token[m++]=ch;

ch=prog[p++];}

token[m++]='';

ch=prog[p--];

syn=10;

for(n=0;n<6;n++)

if(strcmp(token,rwtab[n])==0)/*字元串的比較*/{

syn=n+1;

break;}}

else

if(isdigit(ch))/*ch是數字字元*/{

while(isdigit(ch))/*ch是數字字元*/{

sum=sum*10+ch-'0';

ch=prog[p++];}

ch=prog[p--];

syn=11;}

else

switch(ch){

case'<':m=0;token[m++]=ch;ch=prog[p++];

if(ch=='>'){

syn=21;

token[m++]=ch;}

elseif(ch=='='){

syn=22;

token[m++]=ch;}

else{

syn=20;

ch=prog[p--];}

break;

case'>':m=0;token[m++]=ch;ch=prog[p++];

if(ch=='='){

syn=24;

token[m++]=ch;}

else{

syn=23;

ch=prog[p--];}

break;

case':':m=0;token[m++]=ch;ch=prog[p++];

if(ch=='='){

syn=18;

token[m++]=ch;}

else{

syn=17;

ch=prog[p--];}

break;

case'+':syn=13;token[0]=ch;break;

case'-':syn=14;token[0]=ch;break;

case'*':syn=15;token[0]=ch;break;

case'/':syn=16;token[0]=ch;break;

case'=':syn=25;token[0]=ch;break;

case';':syn=26;token[0]=ch;break;

case'(':syn=27;token[0]=ch;break;

case')':syn=28;token[0]=ch;break;

case'#':syn=0;token[0]=ch;break;

default:syn=-1;}}

main()

{

printf(" Thesignificanceofthefigures: "

"1.figures1to6saidKeyword "

"2. "

"3.figures13to28saidOperators ");

p=0;

printf(" pleaseinputstring: ");

do{

ch=getchar();

prog[p++]=ch;

}while(ch!='#');

p=0;

do{

scaner();

switch(syn){

case11:printf("(%d,%d) ",syn,sum);break;

case-1:printf(" ERROR; ");break;

default:printf("(%d,%s) ",syn,token);

}

}while(syn!=0);

getch();

}

程序測試結果

對源程序beginx:=9:ifx>9thenx:=2*x+1/3;end#的源文件,經過詞法分析後輸出如下圖5-1所示:

具體的你在修改修改吧

java中,棧和堆分別是什麼創建的最好詳細點。。

棧與堆都是Java用來在Ram中存放數據的地方。與C++不同,Java自動管理棧和堆,程序員不能直接地設置棧或堆。
Java 的堆是一個運行時數據區,類的(對象從中分配空間。這些對象通過new、newarray、anewarray和multianewarray等指令建立,它們不需要程序代碼來顯式的釋放。堆是由垃圾回收來負責的,堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,因為它是在運行時動態分配內存的,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由於要在運行時動態分配內存,存取速度較慢。
棧的優勢是,存取速度比堆要快,僅次於寄存器,棧數據可以共享。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本類型的變數(,int, short, long, byte, float, double, boolean, char)和對象句柄。
棧有一個很重要的特殊性,就是存在棧中的數據可以共享。假設我們同時定義:
int a = 3;
int b = 3;
編譯器先處理int a = 3;首先它會在棧中創建一個變數為a的引用,然後查找棧中是否有3這個值,如果沒找到,就將3存放進來,然後將a指向3。接著處理int b = 3;在創建完b的引用變數後,因為在棧中已經有3這個值,便將b直接指向3。這樣,就出現了a與b同時均指向3的情況。這時,如果再令a=4;那麼編譯器會重新搜索棧中是否有4值,如果沒有,則將4存放進來,並令a指向4;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。要注意這種數據的共享與兩個對象的引用同時指向一個對象的這種共享是不同的,因為這種情況a的修改並不會影響到b, 它是由編譯器完成的,它有利於節省空間。而一個對象引用變數修改了這個對象的內部狀態,會影響到另一個對象引用變數。

String是一個特殊的包裝類數據。可以用:
String str = new String("abc");
String str = "abc";
兩種的形式來創建,第一種是用new()來新建對象的,它會在存放於堆中。每調用一次就會創建一個新的對象。
而第二種是先在棧中創建一個對String類的對象引用變數str,然後查找棧中有沒有存放"abc",如果沒有,則將"abc"存放進棧,並令str指向」abc」,如果已經有」abc」 則直接令str指向「abc」。

比較類裡面的數值是否相等時,用equals()方法;當測試兩個包裝類的引用是否指向同一個對象時,用==,下面用例子說明上面的理論。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和str2是指向同一個對象的。

String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用new的方式是生成不同的對象。每一次生成一個。
因此用第二種方式創建多個」abc」字元串,在內存中其實只存在一個對象而已. 這種寫法有利與節省內存空間. 同時它可以在一定程度上提高程序的運行速度,因為JVM會自動根據棧中數據的實際情況來決定是否有必要創建新對象。而對於String str = new String("abc");的代碼,則一概在堆中創建新對象,而不管其字元串值是否相等,是否有必要創建新對象,從而加重了程序的負擔。
另一方面, 要注意: 我們在使用諸如String str = "abc";的格式定義類時,總是想當然地認為,創建了String類的對象str。擔心陷阱!對象可能並沒有被創建!而可能只是指向一個先前已經創建的對象。只有通過new()方法才能保證每次都創建一個新的對象。由於String類的immutable性質,當String變數需要經常變換其值時,應該考慮使用StringBuffer類,以提高程序效率。

java中內存分配策略及堆和棧的比較
2.1 內存分配策略
按照編譯原理的觀點,程序運行時的內存分配有三種策略,分別是靜態的,棧式的,和堆式的.
靜態存儲分配是指在編譯時就能確定每個數據目標在運行時刻的存儲空間需求,因而在編譯時就可以給他們分配固定的內存空間.這種分配策略要求程序代碼中不允許有可變數據結構(比如可變數組)的存在,也不允許有嵌套或者遞歸的結構出現,因為它們都會導致編譯程序無法計算準確的存儲空間需求.
棧式存儲分配也可稱為動態存儲分配,是由一個類似於堆棧的運行棧來實現的.和靜態存儲分配相反,在棧式存儲方案中,程序對數據區的需求在編譯時是完全未知的,只有到運行的時候才能夠知道,但是規定在運行中進入一個程序模塊時,必須知道該程序模塊所需的數據區大小才能夠為其分配內存.和我們在數據結構所熟知的棧一樣,棧式存儲分配按照先進後出的原則進行分配。
靜態存儲分配要求在編譯時能知道所有變數的存儲要求,棧式存儲分配要求在過程的入口處必須知道所有的存儲要求,而堆式存儲分配則專門負責在編譯時或運行時模塊入口處都無法確定存儲要求的數據結構的內存分配,比如可變長度串和對象實例.堆由大片的可利用塊或空閑塊組成,堆中的內存可以按照任意順序分配和釋放.

2.2 堆和棧的比較
上面的定義從編譯原理的教材中總結而來,除靜態存儲分配之外,都顯得很呆板和難以理解,下面撇開靜態存儲分配,集中比較堆和棧:
從堆和棧的功能和作用來通俗的比較,堆主要用來存放對象的,棧主要是用來執行程序的.而這種不同又主要是由於堆和棧的特點決定的:
編程中,例如C/C++中,所有的方法調用都是通過棧來進行的,所有的局部變數,形式參數都是從棧中分配內存空間的。實際上也不是什麼分配,只是從棧頂向上用就行,就好像工廠中的傳送帶(conveyor belt)一樣,Stack Pointer會自動指引你到放東西的位置,你所要做的只是把東西放下來就行.退出函數的時候,修改棧指針就可以把棧中的內容銷毀.這樣的模式速度最快, 當然要用來運行程序了.需要注意的是,在分配的時候,比如為一個即將要調用的程序模塊分配數據區時,應事先知道這個數據區的大小,也就說是雖然分配是在程序運行時進行的,但是分配的大小多少是確定的,不變的,而這個"大小多少"是在編譯時確定的,不是在運行時.
堆是應用程序在運行的時候請求操作系統分配給自己內存,由於從操作系統管理的內存分配,所以在分配和銷毀時都要佔用時間,因此用堆的效率非常低.但是堆的優點在於,編譯器不必知道要從堆里分配多少存儲空間,也不必知道存儲的數據要在堆里停留多長的時間,因此,用堆保存數據時會得到更大的靈活性。事實上,面向對象的多態性,堆內存分配是必不可少的,因為多態變數所需的存儲空間只有在運行時創建了對象之後才能確定.在C++中,要求創建一個對象時,只需用 new命令編制相關的代碼即可。執行這些代碼時,會在堆里自動進行數據的保存.當然,為達到這種靈活性,必然會付出一定的代價:在堆里分配存儲空間時會花掉更長的時間!這也正是導致我們剛才所說的效率低的原因,看來列寧同志說的好,人的優點往往也是人的缺點,人的缺點往往也是人的優點(暈~).

2.3 JVM中的堆和棧
JVM是基於堆棧的虛擬機.JVM為每個新創建的線程都分配一個堆棧.也就是說,對於一個Java程序來說,它的運行就是通過對堆棧的操作來完成的。堆棧以幀為單位保存線程的狀態。JVM對堆棧只進行兩種操作:以幀為單位的壓棧和出棧操作。
我們知道,某個線程正在執行的方法稱為此線程的當前方法.我們可能不知道,當前方法使用的幀稱為當前幀。當線程激活一個Java方法,JVM就會在線程的 Java堆棧里新壓入一個幀。這個幀自然成為了當前幀.在此方法執行期間,這個幀將用來保存參數,局部變數,中間計算過程和其他數據.這個幀在這里和編譯原理中的活動紀錄的概念是差不多的.
從Java的這種分配機制來看,堆棧又可以這樣理解:堆棧(Stack)是操作系統在建立某個進程時或者線程(在支持多線程的操作系統中是線程)為這個線程建立的存儲區域,該區域具有先進後出的特性。
每一個Java應用都唯一對應一個JVM實例,每一個實例唯一對應一個堆。應用程序在運行中所創建的所有類實例或數組都放在這個堆中,並由應用所有的線程共享.跟C/C++不同,Java中分配堆內存是自動初始化的。Java中所有對象的存儲空間都是在堆中分配的,但是這個對象的引用卻是在堆棧中分配,也就是說在建立一個對象時從兩個地方都分配內存,在堆中分配的內存實際建立這個對象,而在堆棧中分配的內存只是一個指向這個堆對象的指針(引用)而已。

❹ 求詳解 pascal 拓撲排序

拓撲排序
有向無迴路圖又稱為dag。對這種有向無迴路圖的拓撲排序的結果為該圖所有頂點的一個線性序列,滿足如果G包含(u,v),則在序列中u出現在v之前(如果圖是有迴路的就不可能存在這樣的線性序列)。一個圖的拓撲排序可以看成是圖的所有頂點沿水平線排成的一個序列,使得所有的有向邊均從左指向右。因此,拓撲排序不同於通常意義上對於線性表的排序。

有向無迴路圖經常用於說明事件發生的先後次序,圖1給出一個實例說明早晨穿衣的過程。必須先穿某一衣物才能再穿其他衣物(如先穿襪子後穿鞋),也有一些衣物可以按任意次序穿戴(如襪子和短褲)。圖1(a)所示的圖中的有向邊(u,v)表明衣服u必須先於衣服v穿戴。因此該圖的拓撲排序給出了一個穿衣的順序。每個頂點旁標的是發現時刻與完成時刻。圖1(b)說明對該圖進行拓撲排序後將沿水平線方向形成一個頂點序列,使得圖中所有有向邊均從左指向右。

下列簡單演算法可以對一個有向無迴路圖進行拓撲排序。

procere Topological_Sort(G);
begin
1.調用DFS(G)計算每個頂點的完成時間f[v];
2.當每個頂點完成後,把它插入鏈表前端;
3.返回由頂點組成的鏈表;
end;
圖1(b)說明經拓撲排序的結點以與其完成時刻相反的順序出現。因為深度優先搜索的運行時間為θ(V+E),每一個v中結點插入鏈表需佔用的時間為θ(1),因此進行拓撲排序的運行時間θ(V+E)。

圖1 早晨穿衣的過程

為了證明演算法的正確性,我們運用了下面有關有向無迴路圖的重要引理。

引理1

有向圖G無迴路當且僅當對G進行深度優先搜索沒有得到反向邊。

證明:

→:假設有一條反向邊(u,v),那麼在深度優先森林中結點v必為結點u的祖先,因此G中從v到u必存在一通路,這一通路和邊(u,v)構成一個迴路。

←:假設G中包含一迴路C,我們證明對G的深度優先搜索將產生一條反向邊。設v是迴路C中第一個被發現的結點且邊(u,v)是C中的優先邊,在時刻d[v]從v到u存在一條由白色結點組成的通路,根據白色路徑定理可知在深度優先森林中結點u必是結點v的後裔,因而(u,v)是一條反向邊。(證畢)

定理1

Topological_Sort(G)演算法可產生有向無迴路圖G的拓撲排序。

證明:

假設對一已知有問無迴路圖G=(V,E)運行過程DFS以確定其結點的完成時刻。那麼只要證明對任一對不同結點u,v∈V,若G中存在一條從u到v的有向邊,則f[v]<f[u]即可。考慮過程DFS(G)所探尋的任何邊(u,v),當探尋到該邊時,結點v不可能為灰色,否則v將成為u的祖先,(u,v)將是一條反向邊,和引理1矛盾。因此,v必定是白色或黑色結點。若v是白色,它就成為u的後裔,因此f[v]<f[u]。若v是黑色,同樣f[v]<f[u]。這樣一來對於圖中任意邊(u,v),都有f[v]<f[u],從而定理得證。(證畢)

另一種拓撲排序的演算法基於以下思想:首先選擇一個無前驅的頂點(即入度為0的頂點,圖中至少應有一個這樣的頂點,否則肯定存在迴路),然後從圖中移去該頂點以及由他發出的所有有向邊,如果圖中還存在無前驅的頂點,則重復上述操作,直到操作無法進行。如果圖不為空,說明圖中存在迴路,無法進行拓撲排序;否則移出的頂點的順序就是對該圖的一個拓撲排序。

下面是該演算法的具體實現:

procere Topological_Sort_II(G);
begin
1 for 每個頂點u∈V[G] do d[u]←0; //初始化d[u],d[u]用來記錄頂點u的入度

2 for 每個頂點u∈V[G] do
3 for 每個頂點v∈Adj[u] do d[v]←d[v]+1; //統計每個頂點的入度

4 CreateStack(s); //建立一個堆棧s

5 for 每個頂點u∈V[G] do
6 if d[u]=0 then push(u,s); //將度為0的頂點壓入堆棧

7 count←0;

8 while (not Empty(s)) do
begin
9 u←top(s); //取出棧頂元素
10 pop(s); //彈出一個棧頂元素
11 count←count+1;
12 R[count]←u; //線性表R用來記錄拓撲排序的結果

13 for 每個頂點v∈Adj[u] do //對於每個和u相鄰的節點v
begin
14 d[v]←d[v]-1;
15 if d[v]=0 then push(v,s); //如果出現入度為0的頂點將其壓入棧
end;
end;

16 if count<>G.size then writeln('Error! The graph has cycle.')
17 else 按次序輸出R;
end;
上面的演算法中利用d[u]來記錄頂點u的入度,第2-3行用來統計所有頂點的入度,第5-6行將入度為0的頂點壓入堆棧,第8-15行不斷地從棧頂取出頂點,將該頂點輸出到拓撲序列中,並將所有與該頂點相鄰的頂點的入度減1,如果某個頂點的入度減至0,則壓入堆棧,重復該過程直到堆棧空了為止。顯而易見該演算法的復雜度為O(VE),因為第2-3行的復雜性就是O(VE),後面8-15行的復雜性也是O(VE)。這個演算法雖然簡單,但是沒有前面一個演算法的效率高。

❺ 編譯原理實驗二 LL(1)分析法

通過完成預測分析法的語法分析程序,了解預測分析法和遞歸子程序法的區別和聯系。使學生了解語法分析的功能,掌握語法分析程序設計的原理和構造方法,訓練學生掌握開發應用程序的基本方法。有利於提高學生的專業素質,為培養適應社會多方面需要的能力。

根據某一文法編制調試 LL(1)分析程序,以便對任意輸入的符號串進行分析。
構造預測分析表,並利用分析表和一個棧來實現對上述程序設計語言的分析程序。
分析法的功能是利用LL(1)控製程序根據顯示棧棧頂內容、向前看符號以及LL(1)分析表,對輸入符號串自上而下的分析過程。

對文法 的句子進行不含回溯的自上向下語法分析的充分必要條件是:
(1)文法不含左遞歸;
(2)對於文法中的每一個非終結符 的各個產生式的候選首符集兩兩不相交,即,若

Follow集合構造:
對於文法 的每個非終結符 構造 的演算法是,連續使用下面的規則,直至每個 不再增大為止:

僅給出核心部分
(1) GrammerSymbol.java

(2) GrammerSymbols.java

(3) Grammer.java

(4) LL1Grammer.java

❻ 誰能解釋一下java中的棧內存和堆內存

2.1 內存分配策略
按照編譯原理的觀點,程序運行時的內存分配有三種策略,分別是靜態的,棧式的,和堆式的.
靜態存儲分配是指在編譯時就能確定每個數據目標在運行時刻的存儲空間需求,因而在編譯時就可以給他們分配固定的內存空間.這種分配策略要求程序代碼中不允許有可變數據結構(比如可變數組)的存在,也不允許有嵌套或者遞歸的結構出現,因為它們都會導致編譯程序無法計算準確的存儲空間需求.
棧式存儲分配也可稱為動態存儲分配,是由一個類似於堆棧的運行棧來實現的.和靜態存儲分配相反,在棧式存儲方案中,程序對數據區的需求在編譯時是完全未知的,只有到運行的時候才能夠知道,但是規定在運行中進入一個程序模塊時,必須知道該程序模塊所需的數據區大小才能夠為其分配內存.和我們在數據結構所熟知的棧一樣,棧式存儲分配按照先進後出的原則進行分配。
靜態存儲分配要求在編譯時能知道所有變數的存儲要求,棧式存儲分配要求在過程的入口處必須知道所有的存儲要求,而堆式存儲分配則專門負責在編譯時或運行時模塊入口處都無法確定存儲要求的數據結構的內存分配,比如可變長度串和對象實例.堆由大片的可利用塊或空閑塊組成,堆中的內存可以按照任意順序分配和釋放.

2.2 堆和棧的比較
上面的定義從編譯原理的教材中總結而來,除靜態存儲分配之外,都顯得很呆板和難以理解,下面撇開靜態存儲分配,集中比較堆和棧:
從堆和棧的功能和作用來通俗的比較,堆主要用來存放對象的,棧主要是用來執行程序的.而這種不同又主要是由於堆和棧的特點決定的:
在編程中,例如C/C++中,所有的方法調用都是通過棧來進行的,所有的局部變數,形式參數都是從棧中分配內存空間的。實際上也不是什麼分配,只是從棧頂向上用就行,就好像工廠中的傳送帶(conveyor belt)一樣,Stack Pointer會自動指引你到放東西的位置,你所要做的只是把東西放下來就行.退出函數的時候,修改棧指針就可以把棧中的內容銷毀.這樣的模式速度最快,當然要用來運行程序了.需要注意的是,在分配的時候,比如為一個即將要調用的程序模塊分配數據區時,應事先知道這個數據區的大小,也就說是雖然分配是在程序運行時進行的,但是分配的大小多少是確定的,不變的,而這個"大小多少"是在編譯時確定的,不是在運行時.
堆是應用程序在運行的時候請求操作系統分配給自己內存,由於從操作系統管理的內存分配,所以在分配和銷毀時都要佔用時間,因此用堆的效率非常低.但是堆的優點在於,編譯器不必知道要從堆里分配多少存儲空間,也不必知道存儲的數據要在堆里停留多長的時間,因此,用堆保存數據時會得到更大的靈活性。事實上,面向對象的多態性,堆內存分配是必不可少的,因為多態變數所需的存儲空間只有在運行時創建了對象之後才能確定.在C++中,要求創建一個對象時,只需用new命令編制相關的代碼即可。執行這些代碼時,會在堆里自動進行數據的保存.當然,為達到這種靈活性,必然會付出一定的代價:在堆里分配存儲空間時會花掉更長的時間!這也正是導致我們剛才所說的效率低的原因,看來列寧同志說的好,人的優點往往也是人的缺點,人的缺點往往也是人的優點(暈~).

2.3 JVM中的堆和棧
JVM是基於堆棧的虛擬機.JVM為每個新創建的線程都分配一個堆棧.也就是說,對於一個Java程序來說,它的運行就是通過對堆棧的操作來完成的。堆棧以幀為單位保存線程的狀態。JVM對堆棧只進行兩種操作:以幀為單位的壓棧和出棧操作。
我們知道,某個線程正在執行的方法稱為此線程的當前方法.我們可能不知道,當前方法使用的幀稱為當前幀。當線程激活一個Java方法,JVM就會在線程的Java堆棧里新壓入一個幀。這個幀自然成為了當前幀.在此方法執行期間,這個幀將用來保存參數,局部變數,中間計算過程和其他數據.這個幀在這里和編譯原理中的活動紀錄的概念是差不多的.
從Java的這種分配機制來看,堆棧又可以這樣理解:堆棧(Stack)是操作系統在建立某個進程時或者線程(在支持多線程的操作系統中是線程)為這個線程建立的存儲區域,該區域具有先進後出的特性。
每一個Java應用都唯一對應一個JVM實例,每一個實例唯一對應一個堆。應用程序在運行中所創建的所有類實例或數組都放在這個堆中,並由應用所有的線程共享.跟C/C++不同,Java中分配堆內存是自動初始化的。Java中所有對象的存儲空間都是在堆中分配的,但是這個對象的引用卻是在堆棧中分配,也就是說在建立一個對象時從兩個地方都分配內存,在堆中分配的內存實際建立這個對象,而在堆棧中分配的內存只是一個指向這個堆對象的指針(引用)而已。

❼ 尋求pascal計算四則混合運算的原理

在編譯原理里有講,利用堆棧.
我來簡單的說下.
程序中有2個棧,一個放數值,一個放操作符

A 程序開始後,第一次的操作符和數值先入棧
B 獲取下一個操作符NextOP
C 把NextOP和棧頂操作符進行優先順序比較

情況1.NextOP優先順序比棧頂操作符要高的話,把NextOP壓入操作符棧.繼續步驟B
情況2.NextOP優先順序比棧頂操作符要低的話,用棧頂運行符,數值棧拿頂的2個值做運算,運算結果壓入數值棧.重復步驟C 如果操作符棧為空,運算結束,步驟D

D 運算結果在數值棧
回答者: cpplyy - 千總 四級 10-8 09:12
1.分治搜索法 每次找到最後運算的符號,對其左右分別深搜到出結果為止
2.堆棧處理法 分數棧與符號棧 先定義常量數組(判斷運算\入棧\錯誤),然後從左到右依次運算 數進數棧 符號與符號棧內比較運算\入棧\錯誤操作 運算從數棧里拿數與後面的計算 然後結果入數棧 最後結果在數棧里
回答者: LXYXYNT - 舉人 四級 10-8 12:50
堆棧就行了
回答者: sd542927172 - 見習魔法師 二級 10-9 20:09
堆棧

表達式的計算應用相當廣泛,比如電力調度系統中的計算遙測、車站票務系統中的票價類型計算公式等。
本文講述中置表達式轉換為後置表達式和後置表達式的求值演算法,並給出實現的C++源代碼,同時給出一個相當簡潔的堆棧C++模板類。

中綴表達式到後綴表達式的轉換
要把表達式從中綴表達式的形式轉換成用後綴表示法表示的等價表達式,必須了解操作符的優先順序和結合性。優先順序或者說操作符的強度決定求值順序;優先順序高的操作符比優先順序低的操作符先求值。 如果所有操作符優先順序一樣,那麼求值順序就取決於它們的結合性。操作符的結合性定義了相同優先順序操作符組合的順序(從右至左或從左至右)。
轉換過程包括用下面的演算法讀入中綴表達式的操作數、操作符和括弧:
1. 初始化一個空堆棧,將結果字元串變數置空。
2. 從左到右讀入中綴表達式,每次一個字元。
3. 如果字元是操作數,將它添加到結果字元串。
4. 如果字元是個操作符,彈出(pop)操作符,直至遇見開括弧(opening parenthesis)、優先順序較低的操作符或者同一優先順序的右結合符號。把這個操作符壓入(push)堆棧。
5. 如果字元是個開括弧,把它壓入堆棧。
6. 如果字元是個閉括弧(closing parenthesis),在遇見開括弧前,彈出所有操作符,然後把它們添加到結果字元串。
7. 如果到達輸入字元串的末尾,彈出所有操作符並添加到結果字元串。

後綴表達式求值
對後綴表達式求值比直接對中綴表達式求值簡單。在後綴表達式中,不需要括弧,而且操作符的優先順序也不再起作用了。您可以用如下演算法對後綴表達式求值:
1. 初始化一個空堆棧
2. 從左到右讀入後綴表達式
3. 如果字元是一個操作數,把它壓入堆棧。
4. 如果字元是個操作符,彈出兩個操作數,執行恰當操作,然後把結果壓入堆棧。如果您不能夠彈出兩個操作數,後綴表達式的語法就不正確。
5. 到後綴表達式末尾,從堆棧中彈出結果。若後綴表達式格式正確,那麼堆棧應該為空。

寫在這里不方便,講解的話+Q244957727
給分拉給分啦~
回答者: wind_teller - 魔法師 四級 10-10 21:57
表達式的計算應用相當廣泛,比如電力調度系統中的計算遙測、車站票務系統中的票價類型計算公式等。
本文講述中置表達式轉換為後置表達式和後置表達式的求值演算法,並給出實現的C++源代碼,同時給出一個相當簡潔的堆棧C++模板類。

中綴表達式到後綴表達式的轉換
要把表達式從中綴表達式的形式轉換成用後綴表示法表示的等價表達式,必須了解操作符的優先順序和結合性。優先順序或者說操作符的強度決定求值順序;優先順序高的操作符比優先順序低的操作符先求值。 如果所有操作符優先順序一樣,那麼求值順序就取決於它們的結合性。操作符的結合性定義了相同優先順序操作符組合的順序(從右至左或從左至右)。
轉換過程包括用下面的演算法讀入中綴表達式的操作數、操作符和括弧:
1. 初始化一個空堆棧,將結果字元串變數置空。
2. 從左到右讀入中綴表達式,每次一個字元。
3. 如果字元是操作數,將它添加到結果字元串。
4. 如果字元是個操作符,彈出(pop)操作符,直至遇見開括弧(opening parenthesis)、優先順序較低的操作符或者同一優先順序的右結合符號。把這個操作符壓入(push)堆棧。
5. 如果字元是個開括弧,把它壓入堆棧。
6. 如果字元是個閉括弧(closing parenthesis),在遇見開括弧前,彈出所有操作符,然後把它們添加到結果字元串。
7. 如果到達輸入字元串的末尾,彈出所有操作符並添加到結果字元串。

後綴表達式求值
對後綴表達式求值比直接對中綴表達式求值簡單。在後綴表達式中,不需要括弧,而且操作符的優先順序也不再起作用了。您可以用如下演算法對後綴表達式求值:
1. 初始化一個空堆棧
2. 從左到右讀入後綴表達式
3. 如果字元是一個操作數,把它壓入堆棧。
4. 如果字元是個操作符,彈出兩個操作數,執行恰當操作,然後把結果壓入堆棧。如果您不能夠彈出兩個操作數,後綴表達式的語法就不正確。
5. 到後綴表達式末尾,從堆棧中彈出結果。若後綴表達式格式正確,那麼堆棧應該為空
在編譯原理里有講,利用堆棧.
我來簡單的說下.
程序中有2個棧,一個放數值,一個放操作符

A 程序開始後,第一次的操作符和數值先入棧
B 獲取下一個操作符NextOP
C 把NextOP和棧頂操作符進行優先順序比較

情況1.NextOP優先順序比棧頂操作符要高的話,把NextOP壓入操作符棧.繼續步驟B
情況2.NextOP優先順序比棧頂操作符要低的話,用棧頂運行符,數值棧拿頂的2個值做運算,運算結果壓入數值棧.重復步驟C 如果操作符棧為空,運算結束,步驟D

D 運算結果在數值棧
.分治搜索法 每次找到最後運算的符號,對其左右分別深搜到出結果為止
2.堆棧處理法 分數棧與符號棧 先定義常量數組(判斷運算\入棧\錯誤),然後從左到右依次運算 數進數棧 符號與符號棧內比較運算\入棧\錯誤操作 運算從數棧里拿數與後面的計算 然後結果入數棧 最後結果在數棧里

閱讀全文

與編譯原理pascal棧式活動記錄相關的資料

熱點內容
linuxwireshark使用教程 瀏覽:656
手機訪問阿里伺服器地址 瀏覽:678
程序員可以干什麼 瀏覽:70
績效考核權重分配演算法 瀏覽:524
android應用logo 瀏覽:898
光遇安卓服墓土商店什麼時候開 瀏覽:566
月收益翻倍的源碼 瀏覽:638
asop源碼放在哪裡 瀏覽:989
電腦伺服器密碼怎麼找 瀏覽:574
jdp轉換pdf 瀏覽:749
把pdf導入iphone 瀏覽:508
米哈游租賃的雲伺服器是哪個 瀏覽:524
android直接打電話 瀏覽:1017
ubuntu停止命令 瀏覽:285
cnc攻絲編程 瀏覽:869
換個手機號碼app怎麼注冊 瀏覽:321
怎麼下載小猴口算app 瀏覽:117
輕鏈app的貨怎麼樣 瀏覽:626
電腦里的u盤如何加密 瀏覽:372
我的世界全部版本伺服器下載地址 瀏覽:50