導航:首頁 > 源碼編譯 > 有哪些存儲分配策略編譯原理

有哪些存儲分配策略編譯原理

發布時間:2022-08-20 23:12:33

『壹』 常見的存儲分配策略有幾種它們都適合於什麼性質的語言

1 靜態分配若在編譯階段就能確定源程序中各個數據實體的存儲空間大小,則可以採用較簡單的靜態存儲管理。適合靜態管理的語言應具備條件:數組上下界是常數、過程調用不允許遞歸、不允許動態建立數據實體。
2棧式分配適用於允許遞歸調用的程序設計語言
3 堆式分配對於允許程序在運行時為變數動態申請和釋放存儲空間的語言,採用堆式分配是最有效的解決方案

『貳』 編譯原理這門課程第五章運行時的存儲空間管理的知識點有哪些

編譯原理這門課第五章運行時的存儲空間管理的知識點包含章節導引,第一節運行時存儲空間的組織和管理,第二節全局存儲分配策略,第三節非局部名字的訪問,第四節參數傳遞,課後練習,。

『叄』 java語言中提及的「堆」主要有什麼用「棧又有什麼用」

Java把內存劃分成兩種:一種是棧內存,另一種是堆內存。在函數中定義的一些基本類型的變數和對象的引用變數都是在函數的棧內存中分配,當在一段代碼塊定義一個變數時,Java就在棧中為這個變數分配內存空間,當超過變數的作用域後,Java 會自動釋放掉為該變數分配的內存空間,該內存空間可以立即被另作它用。

堆內存用來存放由 new 創建的對象和數組,在堆中分配的內存,由 Java 虛擬機的自動垃圾回收器來管理。在堆中產生了一個數組或者對象之後,還可以在棧中定義一個特殊的變數,讓棧中的這個變數的取值等於數組或對象在堆內存中的首地址,棧中的這個變數就成了數組或對象的引用變數,以後就可以在程序中使用棧中的引用變數來訪問堆中的數組或者對象,引用變數就相當於是為數組或者對象起的一個名稱。引用變數是普通的變數,定義時在棧中分配,引用變數在程序運行到其作用域之外後被釋放。而數組和對象本身在堆中分配,即使程序運行到使用 new 產生數組或者對象的語句所在的代碼塊之外,數組和對象本身占據的內存不會被釋放,數組和對象在沒有引用變數指向它的時候,才變為垃圾,不能在被使用,但仍然占據內存空間不放,在隨後的一個不確定的時間被垃圾回收器收走(釋放掉)。

這也是Java比較占內存的原因,實際上,棧中的變數指向堆內存中的變數,這就是 Java 中的指針!

java中內存分配策略及堆和棧的比較

1 內存分配策略

按照編譯原理的觀點,程序運行時的內存分配有三種策略,分別是靜態的,棧式的,和堆式的.

靜態存儲分配是指在編譯時就能確定每個數據目標在運行時刻的存儲空間需求,因而在編譯時就可以給他們分配固定的內存空間.這種分配策略要求程序代碼中不允許有可變數據結構(比如可變數組)的存在,也不允許有嵌套或者遞歸的結構出現,因為它們都會導致編譯程序無法計算準確的存儲空間需求.

棧式存儲分配也可稱為動態存儲分配,是由一個類似於堆棧的運行棧來實現的.和靜態存儲分配相反,在棧式存儲方案中,程序對數據區的需求在編譯時是完全未知的,只有到運行的時候才能夠知道,但是規定在運行中進入一個程序模塊時,必須知道該程序模塊所需的數據區大小才能夠為其分配內存.和我們在數據結構所熟知的棧一樣,棧式存儲分配按照先進後出的原則進行分配。

靜態存儲分配要求在編譯時能知道所有變數的存儲要求,棧式存儲分配要求在過程的入口處必須知道所有的存儲要求,而堆式存儲分配則專門負責在編譯時或運行時模塊入口處都無法確定存儲要求的數據結構的內存分配,比如可變長度串和對象實例.堆由大片的可利用塊或空閑塊組成,堆中的內存可以按照任意順序分配和釋放.

2 堆和棧的比較

上面的定義從編譯原理的教材中總結而來,除靜態存儲分配之外,都顯得很呆板和難以理解,下面撇開靜態存儲分配,集中比較堆和棧:

從堆和棧的功能和作用來通俗的比較,堆主要用來存放對象的,棧主要是用來執行程序的.而這種不同又主要是由於堆和棧的特點決定的:

編程中,例如C/C++中,所有的方法調用都是通過棧來進行的,所有的局部變數,形式參數都是從棧中分配內存空間的。實際上也不是什麼分配,只是從棧頂向上用就行,就好像工廠中的傳送帶(conveyor belt)一樣,Stack Pointer會自動指引你到放東西的位置,你所要做的只是把東西放下來就行.退出函數的時候,修改棧指針就可以把棧中的內容銷毀.這樣的模式速度最快, 當然要用來運行程序了.需要注意的是,在分配的時候,比如為一個即將要調用的程序模塊分配數據區時,應事先知道這個數據區的大小,也就說是雖然分配是在程序運行時進行的,但是分配的大小多少是確定的,不變的,而這個"大小多少"是在編譯時確定的,不是在運行時.

堆是應用程序在運行的時候請求操作系統分配給自己內存,由於從操作系統管理的內存分配,所以在分配和銷毀時都要佔用時間,因此用堆的效率非常低.但是堆的優點在於,編譯器不必知道要從堆里分配多少存儲空間,也不必知道存儲的數據要在堆里停留多長的時間,因此,用堆保存數據時會得到更大的靈活性。事實上,面向對象的多態性,堆內存分配是必不可少的,因為多態變數所需的存儲空間只有在運行時創建了對象之後才能確定.在C++中,要求創建一個對象時,只需用 new命令編制相關的代碼即可。執行這些代碼時,會在堆里自動進行數據的保存.當然,為達到這種靈活性,必然會付出一定的代價:在堆里分配存儲空間時會花掉更長的時間!這也正是導致我們剛才所說的效率低的原因,看來列寧同志說的好,人的優點往往也是人的缺點,人的缺點往往也是人的優點(暈~).

3 JVM中的堆和棧

JVM是基於堆棧的虛擬機.JVM為每個新創建的線程都分配一個堆棧.也就是說,對於一個Java程序來說,它的運行就是通過對堆棧的操作來完成的。堆棧以幀為單位保存線程的狀態。JVM對堆棧只進行兩種操作:以幀為單位的壓棧和出棧操作。

我們知道,某個線程正在執行的方法稱為此線程的當前方法.我們可能不知道,當前方法使用的幀稱為當前幀。當線程激活一個Java方法,JVM就會在線程的 Java堆棧里新壓入一個幀。這個幀自然成為了當前幀.在此方法執行期間,這個幀將用來保存參數,局部變數,中間計算過程和其他數據.這個幀在這里和編譯原理中的活動紀錄的概念是差不多的.

從Java的這種分配機制來看,堆棧又可以這樣理解:堆棧(Stack)是操作系統在建立某個進程時或者線程(在支持多線程的操作系統中是線程)為這個線程建立的存儲區域,該區域具有先進後出的特性。

每一個Java應用都唯一對應一個JVM實例,每一個實例唯一對應一個堆。應用程序在運行中所創建的所有類實例或數組都放在這個堆中,並由應用所有的線程共享.跟C/C++不同,Java中分配堆內存是自動初始化的。Java中所有對象的存儲空間都是在堆中分配的,但是這個對象的引用卻是在堆棧中分配,也就是說在建立一個對象時從兩個地方都分配內存,在堆中分配的內存實際建立這個對象,而在堆棧中分配的內存只是一個指向這個堆對象的指針(引用)而已。

Java 中的堆和棧

Java把內存劃分成兩種:一種是棧內存,一種是堆內存。

在函數中定義的一些基本類型的變數和對象的引用變數都在函數的棧內存中分配。

當在一段代碼塊定義一個變數時,Java就在棧中為這個變數分配內存空間,當超過變數的作用域後,Java會自動釋放掉為該變數所分配的內存空間,該內存空間可以立即被另作他用。

堆內存用來存放由new創建的對象和數組。

在堆中分配的內存,由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, 它是由編譯器完成的,它有利於節省空間。而一個對象引用變數修改了這個對象的內部狀態,會影響到另一個對象引用變數。

『肆』 運行時環境中存儲分配策略包括

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

『伍』 存儲器管理的連續分配存儲管理方式有哪些

連續分配方式.它是指為了一個用戶程序分配一個連續的內存空間.可以分為單一連續分配、固定分區分配、動態分區分配以及動態重定位分區分配四種方式。不過今天我們講的是固定分區分配和動態分區分配。
固定分區分配是最簡單的一種可運行多道程序的存儲管理方式。 一、基本思想:在系統中把用戶區預先劃分成若干個固定分區(每個分區首地址固定,每個分區長度是固定),每個分區可供一個用戶程序獨占使用。注意:每個分區大小可以相同,也可以不相同。 二、主存分配與回收:藉助主存分配表。 三、地址轉換(靜態重定位):物理地址=分區起始地址+邏輯地址。其中劃分分區方法包括分區大小相等和分區大小不等。
動態分區分配是根據進程的實際需要,動態地為之分配內存空間。一、基本思想:按用戶程序需求動態劃分主存供用戶程序使用。(每個分區首地址是動態的,每個分區的長度也是動態的) 二、主存分配與回收-->(1)未分配表(登記未分配出去的分區情況);(2)已分配表(登記已經分配出去的分區情況)。 三、地址轉換:物理地址=分區起始地址+邏輯地址。 四、分區分配演算法:從空閑分區中選擇分區分www.hbbz08.com 配給用戶程序的策略。 (1)首次適應演算法(最先適應)順序查詢為分配表,從表中找出第一個可以滿足作業申請的分區劃分部分分配給用戶作業。 (2)循環首次適應演算法 (3)最佳適應演算法:從空閑分區中找出一個能滿足用戶作業申請的最小空閑分區劃分給用戶作業使用(有利於大作業執行) (4)最壞適應演算法:從空閑分區中挑最大的分區劃分給用戶程序使用(有利於中、小作業執行)

『陸』 編譯原理試題·

Lex和Yacc應用方法(一).初識Lex
草木瓜 20070301
Lex(Lexical Analyzar 詞法分析生成器),Yacc(Yet Another Compiler Compiler
編譯器代碼生成器)是Unix下十分重要的詞法分析,語法分析的工具。經常用於語言分
析,公式編譯等廣泛領域。遺憾的是網上中文資料介紹不是過於簡單,就是跳躍太大,
入門參考意義並不大。本文通過循序漸進的例子,從0開始了解掌握Lex和Yacc的用法。

一.Lex(Lexical Analyzar) 初步示例
先看簡單的例子(註:本文所有實例皆在RetHat linux下完成):
一個簡單的Lex文件 exfirst.l 內容:
%{
#include "stdio.h"
%}
%%
[\n] ;
[0-9]+ printf("Int : %s\n",yytext);
[0-9]*\.[0-9]+ printf("Float : %s\n",yytext);
[a-zA-Z][a-zA-Z0-9]* printf("Var : %s\n",yytext);
[\+\-\*\/\%] printf("Op : %s\n",yytext);
. printf("Unknown : %c\n",yytext[0]);
%%
在命令行下執行命令flex解析,會自動生成lex.yy.c文件:
[root@localhost liweitest]flex exfirst.l
進行編譯生成parser可執行程序:
[root@localhost liweitest]cc -o parser lex.yy.c -ll
[注意:如果不加-ll鏈結選項,cc編譯時會出現以下錯誤,後面會進一步說明。]
/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crt1.o(.text+0x18): In function `_start':
../sysdeps/i386/elf/start.S:77: undefined reference to `main'
/tmp/cciACkbX.o(.text+0x37b): In function `yylex':
: undefined reference to `yywrap'
/tmp/cciACkbX.o(.text+0xabd): In function `input':
: undefined reference to `yywrap'
collect2: ld returned 1 exit status

創建待解析的文件 file.txt:
title
i=1+3.9;
a3=909/6
bcd=4%9-333
通過已生成的可執行程序,進行文件解析。
[root@localhost liweitest]# ./parser < file.txt
Var : title
Var : i
Unknown : =
Int : 1
Op : +
Float : 3.9
Unknown : ;
Var : a3
Unknown : =
Int : 909
Op : /
Int : 6
Var : bcd
Unknown : =
Int : 4
Op : %
Int : 9
Op : -
Int : 333
到此Lex用法會有個直觀的了解:
1.定義Lex描述文件
2.通過lex,flex工具解析成lex.yy.c文件
3.使用cc編譯lex.yy.c生成可執行程序

再來看一個比較完整的Lex描述文件 exsec.l :

%{
#include "stdio.h"
int linenum;
%}
%%
title showtitle();
[\n] linenum++;
[0-9]+ printf("Int : %s\n",yytext);
[0-9]*\.[0-9]+ printf("Float : %s\n",yytext);
[a-zA-Z][a-zA-Z0-9]* printf("Var : %s\n",yytext);
[\+\-\*\/\%] printf("Op : %s\n",yytext);
. printf("Unknown : %c\n",yytext[0]);
%%
showtitle()
{
printf("----- Lex Example -----\n");
}
int main()
{
linenum=0;
yylex(); /* 進行分析 */
printf("\nLine Count: %d\n",linenum);
return 0;
}
int yywrap()
{
return 1;
}
進行解析編譯:
[root@localhost liweitest]flex exsec.l
[root@localhost liweitest]cc -o parser lex.yy.c
[root@localhost liweitest]./parser < file.txt
----- Lex Example -----
Var : i
Unknown : =
Int : 1
Op : +
Float : 3.9
Unknown : ;
Var : a3
Unknown : =
Int : 909
Op : /
Int : 6
Var : bcd
Unknown : =
Int : 4
Op : %
Int : 9
Op : -
Int : 333
Line Count: 4
這里就沒有加-ll選項,但是可以編譯通過。下面開始著重整理下Lex描述文件.l。

二.Lex(Lexical Analyzar) 描述文件的結構介紹
Lex工具是一種詞法分析程序生成器,它可以根據詞法規則說明書的要求來生成單詞識
別程序,由該程序識別出輸入文本中的各個單詞。一般可以分為<定義部分><規則部
分><用戶子程序部分>。其中規則部分是必須的,定義和用戶子程序部分是任選的。

(1)定義部分
定義部分起始於 %{ 符號,終止於 %} 符號,其間可以是包括include語句、聲明語句
在內的C語句。這部分跟普通C程序開頭沒什麼區別。
%{
#include "stdio.h"
int linenum;
%}
(2) 規則部分
規則部分起始於"%%"符號,終止於"%%"符號,其間則是詞法規則。詞法規則由模式和
動作兩部分組成。模式部分可以由任意的正則表達式組成,動作部分是由C語言語句組
成,這些語句用來對所匹配的模式進行相應處理。需要注意的是,lex將識別出來的單
詞存放在yytext[]字元數據中,因此該數組的內容就代表了所識別出來的單詞的內容。
類似yytext這些預定義的變數函數會隨著後面內容展開一一介紹。動作部分如果有多
行執行語句,也可以用{}括起來。
%%
title showtitle();
[\n] linenum++;
[0-9]+ printf("Int : %s\n",yytext);
[0-9]*\.[0-9]+ printf("Float : %s\n",yytext);
[a-zA-Z][a-zA-Z0-9]* printf("Var : %s\n",yytext);
[\+\-\*\/\%] printf("Op : %s\n",yytext);
. printf("Unknown : %c\n",yytext[0]);
%%
A.規則部分的正則表達式
規則部分是Lex描述文件中最為復雜的一部分,下面列出一些模式部分的正則表達式字
符含義:
A-Z, 0-9, a-z 構成模式部分的字元和數字。
- 指定范圍。例如:a-z 指從 a 到 z 之間的所有字元。
\ 轉義元字元。用來覆蓋字元在此表達式中定義的特殊意義,
只取字元的本身。

[] 表示一個字元集合。匹配括弧內的任意字元。如果第一個字
符是^那麼它表示否定模式。例如: [abC] 匹配 a, b, 和C
的任何一個。

^ 表示否定。
* 匹配0個或者多個上述模式。
+ 匹配1個或者多個上述模式。
? 匹配0個或1個上述模式。
$ 作為模式的最後一個字元時匹配一行的結尾。
{ } 表示一個模式可能出現的次數。 例如: A{1,3} 表示 A 可
能出現1次或3次。[a-z]{5} 表示長度為5的,由a-z組成的
字元。此外,還可以表示預定義的變數。

. 匹配任意字元,除了 \n。
( ) 將一系列常規表達式分組。如:{Letter}({Letter}|{Digit})*
| 表達式間的邏輯或。
"一些符號" 字元的字面含義。元字元具有。如:"*" 相當於 [\*]。
/ 向前匹配。如果在匹配的模式中的"/"後跟有後續表達式,
只匹配模版中"/"前面的部分。如:模式為 ABC/D 輸入 ABCD,
時ABC會匹配ABC/D,而D會匹配相應的模式。輸入ABCE的話,
ABCE就不會去匹配ABC/D。

B.規則部分的優先順序

規則部分具有優先順序的概念,先舉個簡單的例子:

%{
#include "stdio.h"
%}
%%
[\n] ;
A {printf("ONE\n");};
AA {printf("TWO\n");};
AAAA {printf("THREE\n");};
%%
此時,如果輸入內容:
[root@localhost liweitest]# cat file1.txt
AAAAAAA
[root@localhost liweitest]# ./parser < file1.txt
THREE
TWO
ONE
Lex分析詞法時,是逐個字元進行讀取,自上而下進行規則匹配的,讀取到第一個A字元
時,遍歷後發現三個規則皆匹配成功,Lex會繼續分析下去,讀至第五個字元時,發現
"AAAA"只有一個規則可用,即按行為進行處理,以此類推。可見Lex會選擇最長的字元
匹配規則。
如果將規則
AAAA {printf("THREE\n");};
改為
AAAAA {printf("THREE\n");};
./parser < file1.txt 輸出結果為:
THREE
TWO

再來一個特殊的例子:
%%
title showtitle();
[a-zA-Z][a-zA-Z0-9]* printf("Var : %s\n",yytext);
%%
並輸入title,Lex解析完後發現,仍然存在兩個規則,這時Lex只會選擇第一個規則,下面
的則被忽略的。這里就體現了Lex的順序優先順序。把這個例子稍微改一下:
%%
[a-zA-Z][a-zA-Z0-9]* printf("Var : %s\n",yytext);
title showtitle();
%%
Lex編譯時會提示:warning, rule cannot be matched.這時處理title字元時,匹配
到第一個規則後,第二個規則就無效了。
再把剛才第一個例子修改下,加深下印象!
%{
#include "stdio.h"
%}
%%
[\n] ;
A {printf("ONE\n");};
AA {printf("TWO\n");};
AAAA {printf("THREE\n");};
AAAA {printf("Cannot be executed!");};
./parser < file1.txt 顯示效果是一樣的,最後一項規則肯定是會忽略掉的。

C.規則部分的使用變數
且看下面示例:
%{
#include "stdio.h"
int linenum;
%}
int [0-9]+
float [0-9]*\.[0-9]+
%%
{int} printf("Int : %s\n",yytext);
{float} printf("Float : %s\n",yytext);
. printf("Unknown : %c\n",yytext[0]);
%%
在%}和%%之間,加入了一些類似變數的東西,注意是沒有;的,這表示int,float分
別代指特定的含義,在兩個%%之間,可以通過{int}{float}進行直接引用,簡化模
式定義。

(3) 用戶子程序部分
最後一個%%後面的內容是用戶子程序部分,可以包含用C語言編寫的子程序,而這些子
程序可以用在前面的動作中,這樣就可以達到簡化編程的目的。這里需要注意的是,
當編譯時不帶-ll選項時,是必須加入main函數和yywrap(yywrap將下後面說明)。如:
...
%%
showtitle()
{
printf("----- Lex Example -----\n");
}
int main()
{
linenum=0;
yylex(); /* 進行Lex分析 */
printf("\nLine Count: %d\n",linenum);
return 0;
}
int yywrap()
{
return 1;
}

三.Lex(Lexical Analyzar) 一些的內部變數和函數
內部預定義變數:
yytext char * 當前匹配的字元串
yyleng int 當前匹配的字元串長度
yyin FILE * lex當前的解析文件,默認為標准輸出
yyout FILE * lex解析後的輸出文件,默認為標准輸入
yylineno int 當前的行數信息
內部預定義宏:
ECHO #define ECHO fwrite(yytext, yyleng, 1, yyout) 也是未匹配字元的
默認動作

內部預定義的函數:
int yylex(void) 調用Lex進行詞法分析
int yywrap(void) 在文件(或輸入)的末尾調用。如果函數的返回值是1,就停止解
析。 因此它可以用來解析多個文件。代碼可以寫在第三段,這
樣可以解析多個文件。 方法是使用 yyin 文件指針指向不同的
文件,直到所有的文件都被解析。最後,yywrap() 可以返回1
來表示解析的結束。

lex和flex都是解析Lex文件的工具,用法相近,flex意為fast lexical analyzer generator。
可以看成lex的升級版本。

相關更多內容就需要參考flex的man手冊了,十分詳盡。

四.關於Lex的一些綜述
Lex其實就是詞法分析器,通過配置文件*.l,依據正則表達式逐字元去順序解析文件,
並動態更新內存的數據解析狀態。不過Lex只有狀態和狀態轉換能力。因為它沒有堆棧,
它不適合用於剖析外殼結構。而yacc增加了一個堆棧,並且能夠輕易處理像括弧這樣的
結構。Lex善長於模式匹配,如果有更多的運算要求就需要yacc了。

『柒』 編譯原理全部的名詞解釋

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

給我分數啊。。。

『捌』 分區存儲管理中常用哪些分配策略

1、固定分區存儲管理
其基本思想是將內存劃分成若干固定大小的分區,每個分區中最多隻能裝入一個作業。當作業申請內存時,系統按一定的演算法為其選擇一個適當的分區,並裝入內存運行。由於分區大小是事先固定的,因而可容納作業的大小受到限制,而且當用戶作業的地址空間小於分區的存儲空間時,造成存儲空間浪費。

一、空間的分配與回收

系統設置一張「分區分配表」來描述各分區的使用情況,登記的內容應包括:分區號、起始地址、長度和佔用標志。其中佔用標志為「0」時,表示目前該分區空閑;否則登記佔用作業名(或作業號)。有了「分區分配表」,空間分配與回收工作是比較簡單的。

二、地址轉換和存儲保護

固定分區管理可以採用靜態重定位方式進行地址映射。

為了實現存儲保護,處理器設置了一對「下限寄存器」和「上限寄存器」。當一個已經被裝入主存儲器的作業能夠得到處理器運行時,進程調度應記錄當前運行作業所在的分區號,且把該分區的下限地址和上限地址分別送入下限寄存器和上限寄存器中。處理器執行該作業的指令時必須核對其要訪問的絕對地址是否越界。

三、多作業隊列的固定分區管理

為避免小作業被分配到大的分區中造成空間的浪費,可採用多作業隊列的方法。即系統按分區數設置多個作業隊列,將作業按其大小排到不同的隊列中,一個隊列對應某一個分區,以提高內存利用率。

2、可變分區存儲管理
可變分區存儲管理不是預先將內存劃分分區,而是在作業裝入內存時建立分區,使分區的大小正好與作業要求的存儲空間相等。這種處理方式使內存分配有較大的靈活性,也提高了內存利用率。但是隨著對內存不斷地分配、釋放操作會引起存儲碎片的產生。

一、空間的分配與回收

採用可變分區存儲管理,系統中的分區個數與分區的大小都在不斷地變化,系統利用「空閑區表」來管理內存中的空閑分區,其中登記空閑區的起始地址、長度和狀態。當有作業要進入內存時,在「空閑區表」中查找狀態為「未分配」且長度大於或等於作業的空閑分區分配給作業,並做適當調整;當一個作業運行完成時,應將該作業佔用的空間作為空閑區歸還給系統。

可以採用首先適應演算法、最佳(優)適應演算法和最壞適應演算法三種分配策略之一進行內存分配。

二、地址轉換和存儲保護

可變分區存儲管理一般採用動態重定位的方式,為實現地址重定位和存儲保護,系統設置相應的硬體:基址/限長寄存器(或上界/下界寄存器)、加法器、比較線路等。

基址寄存器用來存放程序在內存的起始地址,限長寄存器用來存放程序的長度。處理機在執行時,用程序中的相對地址加上基址寄存器中的基地址,形成一個絕對地址,並將相對地址與限長寄存器進行計算比較,檢查是否發生地址越界。

三、存儲碎片與程序的移動

所謂碎片是指內存中出現的一些零散的小空閑區域。由於碎片都很小,無法再利用。如果內存中碎片很多,將會造成嚴重的存儲資源浪費。解決碎片的方法是移動所有的佔用區域,使所有的空閑區合並成一片連續區域,這一技術稱為移動技術(緊湊技術)。移動技術除了可解決碎片問題還使內存中的作業進行擴充。顯然,移動帶來系統開銷加大,並且當一個作業如果正與外設進行I/O時,該作業是無法移動的。

3、頁式存儲管理

基本原理

1.等分內存

頁式存儲管理將內存空間劃分成等長的若干區域,每個區域的大小一般取2的整數冪,稱為一個物理頁面有時稱為塊。內存的所有物理頁面從0開始編號,稱作物理頁號。

2.邏輯地址

系統將程序的邏輯空間按照同樣大小也劃分成若干頁面,稱為邏輯頁面也稱為頁。程序的各個邏輯頁面從0開始依次編號,稱作邏輯頁號或相對頁號。每個頁面內從0開始編址,稱為頁內地址。程序中的邏輯地址由兩部分組成:

邏輯地址
頁號p
頁內地址 d

3.內存分配

系統可用一張「位示圖」來登記內存中各塊的分配情況,存儲分配時以頁面(塊)為單位,並按程序的頁數多少進行分配。相鄰的頁面在內存中不一定相鄰,即分配給程序的內存塊之間不一定連續。

對程序地址空間的分頁是系統自動進行的,即對用戶是透明的。由於頁面尺寸為2的整數次冪,故相對地址中的高位部分即為頁號,低位部分為頁內地址。

3.5.2實現原理

1.頁表

系統為每個進程建立一張頁表,用於記錄進程邏輯頁面與內存物理頁面之間的對應關系。地址空間有多少頁,該頁表裡就登記多少行,且按邏輯頁的順序排列,形如:

邏輯頁號
主存塊號

0
B0

1
B1

2
B2

3
B3

2.地址映射過程

頁式存儲管理採用動態重定位,即在程序的執行過程中完成地址轉換。處理器每執行一條指令,就將指令中的邏輯地址(p,d)取來從中得到邏輯頁號(p),硬體機構按此頁號查頁表,得到內存的塊號B』,便形成絕對地址(B』,d),處理器即按此地址訪問主存。

3.頁面的共享與保護

當多個不同進程中需要有相同頁面信息時,可以在主存中只保留一個副本,只要讓這些進程各自的有關項中指向內存同一塊號即可。同時在頁表中設置相應的「存取許可權」,對不同進程的訪問許可權進行各種必要的限制。

4、段式存儲管理

基本原理

1.邏輯地址空間

程序按邏輯上有完整意義的段來劃分,稱為邏輯段。例如主程序、子程序、數據等都可各成一段。將一個程序的所有邏輯段從0開始編號,稱為段號。每一個邏輯段都是從0開始編址,稱為段內地址。

2.邏輯地址

程序中的邏輯地址由段號和段內地址(s,d)兩部分組成。

3.內存分配

系統不進行預先劃分,而是以段為單位進行內存分配,為每一個邏輯段分配一個連續的內存區(物理段)。邏輯上連續的段在內存不一定連續存放。

3.6.2實現方法

1.段表

系統為每個進程建立一張段表,用於記錄進程的邏輯段與內存物理段之間的對應關系,至少應包括邏輯段號、物理段首地址和該段長度三項內容。

2.建立空閑區表

系統中設立一張內存空閑區表,記錄內存中空閑區域情況,用於段的分配和回收內存。

3.地址映射過程

段式存儲管理採用動態重定位,處理器每執行一條指令,就將指令中的邏輯地址(s,d)取來從中得到邏輯段號(s),硬體機構按此段號查段表,得到該段在內存的首地址S』, 該段在內存的首地址S』加上段內地址d,便形成絕對地址(S』+d),處理器即按此地址訪問主存。

5、段頁式存儲管理

頁式存儲管理的特徵是等分內存,解決了碎片問題;段式存儲管理的特徵是邏輯分段,便於實現共享。為了保持頁式和段式上的優點,結合兩種存儲管理方案,形成了段頁式存儲管理。

段頁式存儲管理的基本思想是:把內存劃分為大小相等的頁面;將程序按其邏輯關系劃分為若干段;再按照頁面的大小,把每一段劃分成若干頁面。程序的邏輯地址由三部分組成,形式如下:

邏輯地址
段號s
頁號p
頁內地址d

內存是以頁為基本單位分配給每個程序的,在邏輯上相鄰的頁面內存不一定相鄰。

系統為每個進程建立一張段表,為進程的每一段各建立一張頁表。地址轉換過程,要經過查段表、頁表後才能得到最終的物理地址。

『玖』 編譯原理中常見的存儲分配策略有哪些

靜態存儲分配策略和動態存儲分配策略,動態存儲分配時可採用棧式動態存儲分配和堆式動態存儲分配

『拾』 靜態存儲分配和動態存儲分配之間有什麼不同 編譯原理

動態存儲方式
所謂動態存儲方式是指在程序運行期間根據需要進行動態的分配存儲空間的方式。動態存儲變數是在程序執行過程中,使用它時才分配存儲單元,
使用完畢立即釋放。
典型的例子是函數的形式參數,在函數定義時並不給形參分配存儲單元,只是在函數被調用時,才予以分配,
調用函數完畢立即釋放。如果一個函數被多次調用,則反復地分配、
釋放形參變數的存儲單元。
靜態存儲方式
所謂靜態存儲方式是指在程序編譯期間分配固定的存儲空間的方式。該存儲方式通常是在變數定義時就分定存儲單元並一直保持不變,
直至整個程序結束。全局變數,靜態變數等就屬於此類存儲方式。
總結
從以上分析可知,
靜態存儲變數是一直存在的,
而動態存儲變數則時而存在時而消失。我們又把這種由於變數存儲方式不同而產生的特性稱變數的生存期。
生存期表示了變數存在的時間。
生存期和作用域是從時間和空間這兩個不同的角度來描述變數的特性,這兩者既有聯系,又有區別。
一個變數究竟屬於哪一種存儲方式,
並不能僅從其作用域來判斷,還應有明確的存儲類型說明。

閱讀全文

與有哪些存儲分配策略編譯原理相關的資料

熱點內容
windows下編譯python 瀏覽:607
linux藍牙連接 瀏覽:898
安卓qq郵箱格式怎麼寫 瀏覽:429
如何電信租用伺服器嗎 瀏覽:188
編程中計算根號的思維 瀏覽:181
可愛的程序員16集背景音樂 瀏覽:446
軟體代碼內容轉換加密 瀏覽:795
什麼app看電視不要錢的 瀏覽:16
烏班圖怎麼安裝c語言編譯器 瀏覽:278
plc通訊塊編程 瀏覽:923
我的世界伺服器怎麼清地皮 瀏覽:421
ftp伺服器如何批量改名 瀏覽:314
網易我的世界伺服器成員如何傳送 瀏覽:268
公司雲伺服器遠程訪問 瀏覽:633
法哲學pdf 瀏覽:637
清大閱讀app是什麼 瀏覽:447
怎麼用qq瀏覽器整體解壓文件 瀏覽:586
肺組織壓縮15 瀏覽:270
安卓手機為什麼換電話卡沒反應 瀏覽:797
諸子集成pdf 瀏覽:340