第一章
Language Evaluation Criteria
-
可讀性
正交性:指只用該語言的一組相對少量的基本結構,經過
相對少的結合步驟,就可構成該語言的控制結構和數據結
構。而且,它的基本結構的任何組合都是合法和有意義的。 -
可寫性
-
可靠性
-
代價
Influences on Language Design
計算機體系結構和編程方法
Language Categories
-
命令式語言
C,Pascal
-
函數式語言
LISP,Scheme
-
邏輯語言
Prolog
-
面向對象語言
C++,Java
Implementation Methods
編譯,C,COBLO,Ada 瓶頸,處理器和內存速度不匹配。
第二章
第一種編譯式的高級語言:Fortran
邁向成熟的第一步:ALGOL 60
數據抽象的開始:SIMULA 67
歷史上規模最大的語言設計:Ada
面向對象的程序設計:Smalltalk
第一種函數式程序設計語言:LISP
第五章
Names
關鍵字是程序設計語言中的一種字,它只在特定的上下文里是特殊的。Fortran語言中的特殊字就是關鍵字。
保留字是程序設計語言中的特殊字,它不能用作名字。作為語言設計的選擇,保留字比關鍵字優越,因為重新定義關鍵字的功能會導致混淆。
Variables
變量是存儲地址的名字,可以使用6種屬性來刻畫一個變量:名字、地址、數值、類型、生存期、作用域。
變量的地址是與這個變量相關聯的存儲地址。
有時也將變量地址稱為變量的左值,這是因為變量通常位於賦值語句的左邊。
多個變量可以具有同一個地址,當用多個變量名來訪問單個存儲地址時,這些變量名就成為別名。
一個變量的值是與這個變量相關聯的存儲單元的內容。變量的值有時被稱為變量的右值,因為變量被用於賦值語句的右邊時,則會要求這種變量值。要取得右值必須首先確定左值。
The Concept of Binding
綁定是一種關聯,如存在於屬性與實體之間的關聯,或者操作與符號之間的關聯。在程序中引用一個變量之前,變量必須被綁定到一種數據類型之上。這種綁定具有的兩個重要方面是指定類型的方式以及綁定發生的時間。可以通過一些顯示或隱式的聲明來靜態指定變量的類型。
顯式聲明是程序中的一條說明語句,它列出一批變量名並指明這些變量的特定類型。隱式聲明則是通過默認協定的方法而不是聲明語句將變量與類型相關聯。在隱式聲明的情況下,變量名在程序中的第一次出現即構成了它的隱式聲明。顯式聲明與隱式聲明都產生對類型的靜態綁定。
在使用動態類型綁定時,變量的類型不是由聲明語句來說明的,也不能夠通過名字的拼法來確定,而是在賦值語句給變量賦值時,變量才與類型相綁定。在執行賦值語句時,被賦值的變量與賦值語句右邊的表達式的值的類型相綁定。
如果一種綁定的第一次出現是在運行之時,並且在整個程序的執行過程中保持不變,我們稱這種綁定為靜態的。
如果一種綁定的第一次出現是在運行時期間,或者這種綁定可以在程序執行中被改變,我們稱這種綁定為動態的。
Type Checking
類型檢測是保證一個操作符的所有操作數都具有相互兼容類型的措施。兼容類型是對操作符而言為合法的類型,或者在語言規定的允許下,能夠被編譯器或解釋器產生的代碼隱式地轉換成為合法的類型,這種自動類型的轉換被稱為強制轉換。
Strong Typing
只要某個程序設計語言總能夠發現其程序中的類型錯誤,我們就定義這種程序設計語言為強類型的。這就要求能夠在編譯時或運行時確定所有操作數的類型。強類型化的重要性在於它能夠發現所有因為變量的誤用而導致的類型錯誤。一種強類型的語言也允許在運行時檢測能夠存儲多個類型值的變量中不正確類型值的使用。
Type Compatibility
假如在一個表達式中,一種類型的操作數被另一種類型的操作數替代而無強制性,那么這兩種類型是等價的。類型等價是類型兼容的一種嚴格形式——沒有強制性的兼容。
有兩種不同的類型等價方法:名字等價以及結構等價。
名字類型等價意味着:兩個變量具有按名等價的類型當且僅當它們一起被說明,或者用相同的類型標識符說明,即對應的類型名相同。
結構類型等價意味着:兩個變量具有等價的類型,如果它們的類型具有完全相同的結構。(兩個變量具有按結構等價的類型當且僅當變量的所有分量有相同類型的值集。)
按定義等價意味着:兩個變量具有按定義等價的類型當且僅當兩個變量有完全相同的類型(包括相同的域名)。除類型名外的其他特征均相同者即視為按定義等價。
Scope
作用域是理解變量的最重要因素之一。程序變量的作用域是語句的一個范圍,在這個范圍之內變量為可見的。如果一個變量在一條語句中可以被引用,這個變量即在這條語句中為可見的。
Sope and Lifetime
有時,變量的作用域和生命期似乎是相關的,但卻是不同的概念。例如在C和C++中,一個在函數中由修飾符static聲明的變量被靜態地綁定於函數的作用域,同時也被靜態地綁定於存儲空間。所以這個變量的作用域是靜態的,並且對這個函數為局部的,但它的生存期卻擴展到了整個程序的執行期間。
Referencing Environments
一條語句的引用環境是這條語句中所有可見變量的集合。在靜態作用域的語言中,一條語句的引用環境是在它的局部作用域所聲明的變量和在它的祖先作用域所聲明的所有可見變量的集合。
在動態作用域的語言中,一條語句的引用環境是局部聲明的變量,加上當前活躍的其他子程序中的所有變量。一些活躍子程序中的變量可能對引用環境為隱藏的。最近的子程序活動可以具有變量聲明,它隱藏了前一個子程序活動中具有的同名變量。
第六章
Pointer Problems
懸掛指針,或懸掛引用,是一個包含了已解除分配的堆動態變量地址的指針。
懸掛指針是危險的,首先,懸掛指針指向的位置可能已經被重新分配給一個新的堆動態變量,此外,如果是使用懸掛指針來改變堆動態變量,新堆動態變量的值就會被破壞,最后,這個位置很有可能當前暫時被管理系統所使用
丟失的堆動態變量是一個已經分配的堆動態變量,用戶程序不可以再對它進行訪問。常常稱這種變量為垃圾,因為對於它們的原始目的,它們不再有用,並且它們也不可以為了某種新的用途通過程序給以重新分配。
第七章
Arithmetic Expressions
- Operand Evaluation Order
如果一個操作符的兩個操作數都沒有副作用,那么操作數的求值順序是無所謂的。
- Side effects
當函數改變它的一個參數或者一個全局變量時,就會產生函數的副作用(或函數副作用)。(全局變量是被聲明於函數之外,但可以在函數中訪問的變量。)
Overloaded Operators
一個操作符的多種用途被稱為操作符重載。
兩個問題:使用同一個符號進行兩種完全不相關的操作有損於可讀性;編譯器很難檢測出錯誤。
第九章
Introduction
一種程序設計語言可以包括兩種基本的抽象設施:過程抽象與數據抽象。過程抽象是所有程序設計語言中的主要概念。
第一台可編程的計算機是建造於20世紀40年代的Babbage的分析引擎(Analytical Engine ) ,它具有在程序中的一些不同位置上重復使用一系列指令卡片的能力。而現代的程序設計語言是將這樣的語句系列編寫成為子程序。這種重復使用導致了在幾個不同方面的節省,從存儲空間到程序的編碼時間。這提高了子程序的可讀性。
Fundamentals of Subprograms
子程序定義描述的是子程序的接口以及子程序的抽象行為。
子程序的參數描繪(有時也稱為簽名)是它所具有的形參的數目、次序以及類型。一個子程序的協議是這個子程序的參數描繪加上它的返回類型,如果它是函數的話。
子程序通常描述計算。有兩種方法讓子程序能夠獲取它所要處理的數據:通過對非局部變量的直接訪問(在別處被聲明,但在子程序中可見),或者通過參數傳遞。
參數傳遞比對非局部變量的直接訪問更靈活。
對於非局部變量的大量訪問,將導致可靠性程度的降低。
子程序首部中的參數被稱為形參。在大多數情況下,只有當子程序被調用時,它們才與存儲空間相綁定,並且這種綁定通常經過了一些其他的程序變量。
子程序的調用語句必須包括子程序的名稱,以及一組將與子程序中的形參相綁定的參數。這些參數被稱為實參。
實參與形參之間的對應是簡單地按位置進行的,這樣的一些參數被稱為位置參數。
關鍵字參數:將一個與實參相綁定的形參的名稱與這個實參在一起說明。關鍵字參數的優越性是它們能夠以任何順序出現干實參表中。這種形式的唯一限制是,在一個關鍵字參數出現在列表中以后,必須將所有的其余參數關鍵字化。之所以這是有必要的,因為在關鍵字參數出現之后,參數位置的順序就可能不再遵循原來的定義。
Local Referencing Environments
定義於子程序內部的變量被稱為局部變量,因為這些變量的作用域通常就限定於定義他們的子程序之中。
局部變量可以為靜態的,也可以為棧動態的。如果局部變量是棧動態的,當子程序開始執行之時,這些變量就與存儲空間相綁定,並在執行終止時解除這種綁定。
棧動態的局部變量具有很多優點,其中的主要優點是它們為子程序提供了靈活性。棧動態局部變量的另一個優點是,在活躍子程序中的局部變量的存儲空間可以與所有在非活躍子程序中的局部變量共享。
棧動態局部變量的主要缺點如下:首先,對於子程序的每一次調用,這種變量所需要的存儲空間分配、初始化(當必要時)以及變量解除分配,都有時間上的代價。其次,對於棧動態局部變量的存取必須是間接的;而對於靜態變量的存取則可以是直接的。最后一點,具有棧動態局部變量的子程序不是歷史敏感的;也就是說,它們不能夠在調用之間保持局部變量的數據值。
Parameter-Passing Methods
-
pass-by-value
當參數是按值傳遞時,實參的值將被用來為與其相對應的形參設定初值,然后這個形參的行為就像是子程序中的局部變量,並由此實現了輸入型的語義。通常將按值傳遞實現為數據的復制,因為采用這種方法通常具有較高的存取效率。
-
pass-by-result
按結果傳遞是用於輸出型參數的一種實現模式。當一個參數被按結果傳遞時,並沒有將值傳遞到子程序。相對應的形參的行為就如同一個局部變量,但是在將控制返回到調用程序之前,形參值被傳遞給調用程序的實參,顯然,這個實參必須是一個變量。(如果這個實參是一個字面常量或者一個表達式,調用程序怎么能夠引用計算結果呢?)
按結果傳遞模式中的一個額外的問題是可能會出現實參沖突,使用按結果傳遞的方式可能出現的另一個問題是實現人員可以選擇在兩個不同時刻對實參的地址求值:即在調用時,或在返回時。
-
Pass-by-Reference
按引用傳遞是對輸入輸出型參數的第二種實現模式。按引用傳遞的方式不像按值與結果傳遞那樣,將數據值來回地復制;按引用傳遞的方式是給被調用子程序傳遞一條存取途徑,通常就是一個地址。這給被調用子程序提供了對實參存儲單位的存取途徑。因此允許被調用子程序從調用程序單位中存取實參。在實際效果上,被調用子程序就共享了這個實參。
-
Pass-by-Name
按名傳遞是一種輸入輸出型參數的傳遞方法,它不對應於單個的實現模式。當參數是按名傳遞時,對於子程序中的所有情形,實參實際上都以文本形式替代了與它相對應的形參。這與迄今為止討論過的方法相當不同。在前面的情況下,形參在子程序調用時被綁定於實際的值或者地址。而一個按名傳遞的形參,是在子程序調用時被綁定於一種存取方法,而這個形參對於值或者對於地址的實際綁定,則被推遲到對形參賦值或者形參被引用以后。
Overloaded Subprograms
重載子程序是與另一個在相同引用環境中的子程序具有相同名稱的子程序。每一個重載子程序的版本必須只具有一個協議,也就是說,它必須與子程序的其他版本在參數的數目、參數的順序或者參數的類型上不相同;如果它是一個函數的話,則是返回的類型不相同。
C++、Java、Ada以及C#都包括了預定義的重載子程序。
在Ada中,是通過一個重載函數返回的類型來區分調用的是哪一個版本。
Separate and Independent Compilation
分別編譯意味着編譯單元可以在不同的時間被編譯,但是如果其中一個訪問或使用了另一個的任何實體,那么它們的編譯就不是彼此獨立的。
在獨立編譯的情況下、程序單元可以在沒有任何其他程序單元信息的情況下進行編譯。
Accessing Nonlocal Environments
子程序的非局部變量是那些在子程序中可見但沒有局部聲明的變量。
全局變量是指在所有程序單元中可見的變量。
第十章
The General Semantics of Calls and Returns
如果局部變量是非靜態的,這種調用必須對在被調用子程序中聲明的局部變量進行存儲空間的分配,還必須將這些變量與所分配的存儲空間相綁定。它必須保留調用程序單位的執行狀態。執行狀態是任何需要重新啟動調用程序單元的狀態。調用過程必須安排將控制轉移到子程序的代碼,並且在子程序執行結束后必須確保能夠將控制返回到正確的位置。最后,如果這種語言支持嵌套子程序,調用過程還必須產生某種機制,以提供對被調用子程序為可見的、非局部變量的訪問。
如果這種子程序具有輸出型參數,並且是通過復制來實現的,那么返回過程的第一個動作就是將形參的局部值轉移到相關聯的實參上。下一步,它必須將局部變量使用的存儲空間解除分配,並且恢復調用程序的執行狀態。如果這種語言支持嵌套子程序的話,還必須采取一些行動,將非局部引用的機制返回到調用之前的狀態。最后,必須將控制返回到調用程序。
Implementing FORTRAN 77 Subprograms
子程序中的非代碼部分的格式(或布局)被稱為活動記錄,因為這一部分僅僅描述子程序的活動期間或執行期間的有關數據。活動記錄的形式是靜態的。一個活動記錄實例是活動記錄的一個具體示例,它是活動記錄形式的一組數據。
Implementing Subprograms in ALGOL-like Languages
返回地址通常包括了一個指向指令的指針和調用語句之后那條指令的偏移地址,該指令后接着在調用程序單元的代碼段中的調用。
靜態鏈接指向活動的底部。靜態鏈接有時也被稱為靜態作用域指針,它指向靜態父輩活動的活動記錄實例的低層。
動態連接是一個指向調用程序活動記錄實例頂端的指針。
靜態鏈是棧中活動記錄實例的靜態連接的一個鏈。
靜態深度為一個與靜態作用域相關的整數,它表示一個作用域從最外層作用域開始所嵌套的深度。
在對變量x的非局部引用中,達到正確的活動記錄實例所需要的靜態鏈長度,正好是包含x引用的過程的靜態深度與包含x聲明的過程的靜態深度之差。這個差被稱為引用的嵌套深度或者鏈偏移。
第十一章
The Concept of Abstraction
抽象是對於實體的一種觀念或者實體的一種表示,它僅僅包括這個實體的最重要的屬性。
在程序設計語言的世界里,抽象是對抗程序設計復雜性的一種武器,其目的是簡化程序設計的過程。因為抽象允許程序人員將注意力集中於基本屬性而忽略次要的屬性,因而它是一種有效的武器。
當代程序設計語言中的兩類基本抽象是過程抽象和數據抽象。
Encapsulation
封裝是一組子程序和它們操作的數據。
Introduction to Data Abstraction
從語法的角度而言,抽象數據類型是一個封裝,它僅僅包括一種特定數據類型的數據表示,以及一些給這種類型提供操作的子程序。
第十三章
Introduction
存在着兩種並發單位的控制。最普通的一種是假設存在着多個處理器可供使用,並且來自相同程序的幾個程序單位同時地運行,這就是物理並發。一種稍微放寬的並發概念是允許程序員和應用軟件假設:存在着多個處理器提供真實的並發;然而事實上,程序是在單處理器上被分時地執行。這種形式被稱為邏輯並發。
Introduction to Subprogram-Level Concurrency
任務可以通過共享非局部變量、消息傳遞或是參數,來與其他的任務進行通訊。如果一個任務不以任何方式與程序中的其他任務施行通訊,或者是它不影響任何其他任務的執行,我們就稱這個任務是不相關的(disjoint)。因為多個任務常常一起工作來產生一些模擬或者是解決一些問題,因而它們是相關的;並且它們必須使用某種通訊的方式來同步彼此的執行或者共享數據,或者是這兩種方式皆而有之。
同步(synchronization)是一種控制任務執行順序的機制。當一些任務共享數據時,需要有兩種類型的同步:即合作同步與競爭同步。當任務A在繼續它的執行之前,如果它必須等待任務B完成某種特定活動,在任務A與任務B之間就需要合作同步(cooperation synchronization)。當這兩個任務都需要不可能同時使用的某種資源時,這兩個任務之間需要競爭同步(competition synchronization)。
Semaphores
信號量(semaphore)是一種數據結構;它由一個整數和一個存儲任務描述符的隊所組成Monitors
將共享數據上的所有同步操作包括進一個程序單位之中。
管程最重要的特性之一,是共享數據居於管程之中而不是居於任何客戶單位之中。這樣,程序人員就不需要通過使用信號量或者是其他的機制,來同步共享數據的互斥訪問。因為所有的訪問都發生在管程中,所以能夠將管程實現為任何時刻只允許一個訪問的結構,從而保證同步訪問。
Message Passing
如果任務A需要發送一個消息給任務B,並且任務B願意接收,消息就能夠被傳遞過來。這種實際的傳輸被稱為會合(rendezvous)。注意,會合只能夠發生在發送者和接收者都希望它發生的時候。