資電學院 計算機概論 F7810 第三章 程式語言 陳邦治編著 旗標出版社
本章重點 程式語言是由一組系統化的符號所構成之集合 使用程式語言的目的則是利用這些符號來表達某種機器解決特定問題的步驟 程式語言設計的目標則是為了達到簡潔(simplicity)的要求 學習程式語言的主要目的 可增進對程式語言的瞭解 可改進設計程式之架構 增進程式執行之效率 可選擇適用的語言 較易學習與設計新的語言
大綱 程式語言的分類 近代常見程式語言簡介 物件導向程式語言 高階語言處理器 程式語言的資料型態 變數的定義 繫結 完全計算與捷徑計算 變數引用規則 程式設計基本觀念 程式設計基本結構 副程式與參數傳遞的處理方式 3 3
程式語言的分類 程式語言依照出現的先後次序共可分成五代 機器語言(machine language) 組合語言(assembly language) 高階語言(high level language) 極高階語言 自然語言(nature language)
機器語言 機器語言的指令與資料均由二進碼所組成,因此利用機器語言所寫成的程式段不需經由語言處理器的處理便可直接在機器上執行 機器語言最難學習,不易使用 機器相關性(machine dependence)高
組合語言 組合語言的指令稱為助憶碼(mnemonic code) 機器指令(machine operation) 虛擬指令(pseudo operation) 利用組合語言所寫成的程式段必需經由組譯程式(assembler)的處理才可在機器上執行 機器語言與組合語言合稱為低階語言 若將組合語言與機器語言做一比較,組合語言的可讀性較佳,較容易學習,但是程式執行的效率則較差
高階語言 高階語言又稱為程序導向語言(procedure oriented language) 利用高階語言寫成的程式碼必須經過編譯程式(compiler)或直譯程式(interpreter)處理過後方可執行 如Pascal、C、C++、Basic,Fortran與Cobol
極高階語言 極高階語言又稱為問題導向語言(problem oriented language) 如SQL (structured query language)
自然語言 自然語言又稱為知識庫語言(knowledge based language) 語法十分接近人類日常生活所用的語言,如英文、日文或中文
近代程式語言簡介 Fortran Cobol Basic Pascal C/C++ Java Lisp 及Prolog
Fortran Fortran (FORmula TRANslator language)是IBM的John Backus在1950年代中期所開發 第一個高階語言 主要是針對科學計算而設計 具固定格式(當時Fortran規定程式必須從第七行開始寫起) 首創了輸出入格式化(I/O format)的觀念 允許「隱含性變數」(implicit variable),如變數的第一個字元為I,J,K,L,M,N時該變數可不經宣告即內定為整數型態
Cobol Cobol(COmmon Business Oriental Language)發展於1960~1970年代 由美國防部贊助開發完成 主要用於商業資料處理 語法傾向自然語言 首創以雜訊字(noise word)觀念來編寫程式碼
Basic Basic語言(Beginner‘s All-purpose Symbolic Instruction Code)在1960年代中期發展 當適合初學者使用的語言 語法結構簡單,操作也相當容易 交談式(interactive)的語言 利用直譯器(interpreter)來處理程式
Pascal 1975年一個以數學家Blaise Pascal之名命名的程式語言Pascal誕生 採區塊結構(block structure)來寫作程式 首創集合(set)資料型態供程式設計師使用 最大的優勢是具備嚴謹的語法結構,讓使用者不容易犯錯,非常適合教學用途
C語言 C語言是由貝爾實驗室於1970年代所發展出來 採區塊結構,具高可攜性,因此適合發展系統程式 利用C語言撰寫的程式相同字元的大小寫(如A及a)會被視為是不同的符號
C++語言 C++是C語言的物件導向版程式開發工具 C++ 語言是由 Bjarne Stroustrup 在貝爾實驗室中設計而得
Java Java語言是由Sun Microsystems沿襲了C語言的語法並加入許多新的程式結構元素發展而成的物件導向程式語言 指標(pointer)資料型態 多重繼承(multiple inheritance) 運算子覆載(operator overloading)等功能 Java語言允許其程式段能夠透過網路系統到另一個機器平台上執行
LISP LISP(LISt Processing language)是由麻省理工學院於1950年代末期發展 利用S運算式(S expression)做為主要的資料結構 記憶體管理法採用垃圾收集法(garbage collection) 採用「劍橋波蘭式」(Cambridge polish notation)作為算術運算式 一般被稱為人工智慧領域的低階語言
PROLOG PROLOG是由Alan Colmeraure於1970年代初期發展出來 為邏輯式程式語言 一般被稱為人工智慧領域的高階語言
物件導向程式語言 物件導向程式語言(Object Oriented Programming Language;OOPL)是由「抽象資料型態」(abstract data type)與「資料抽象化」(data abstraction)的觀念發展而來 「抽象資料型態」是指資料型態可區分為實作(implementation)及使用者介面(user interface)二部分 而「資料抽象化」則是指將資料型態區分為實作與使用者介面的動作即稱為資料抽象化
物件導向程式語言三大特徵 資訊隱藏或稱為封裝(encapsulation) 繼承(inheritance)能力 多面性(polymorphism)
封裝 封裝主要的目的就是期望能將不希望給外界知道的資訊,隱藏起來也就是達到「資訊隱藏」(information hidden)的目的 通常在寫作程式時可以利用將變數宣告為不同等級便可達到「資訊隱藏」的效果
繼承 繼承是指程式語言可利用已建立好的類別(class)來產生新的類別,依此方式產生的新類別將可繼承所有原來類別的特性並可依需要新增或刪除特定的功能 「繼承」是類別要完成擴充目的的基本要件 C++ 語言允許 1對1,1對多,多對1的繼承方式 JAVA語言僅允許允許1對1的繼承方式 若程式語言具備「繼承」能力則程式的可重用性將較高,較易擴充且較易維護
多面性 多面性代表具相同名稱的函式,但卻具有不同的功能 如C++語言允許右方程式段之寫法 右方程式段中有四個print函式,雖然函式的名稱都是print,但是因為參數的型態不同,因此會被視為是四個不同的函式
高階語言的處理器 高階語言的處理器主要的作用即是將利用高階語言寫成的程式段翻譯成機器可處理的碼 主要可分成編譯器(compiler)及直譯器(interpreter)二類 編譯器(也可稱為編譯程式)會對原始程式碼中的每一條敘述,按照先後順序均做一次之處理,並產生對應的目的碼 直譯器(也可稱為直譯程式)會對原始程式碼中的敘述,按照執行的先後順序做處理,並直接產生程式執行結果
編譯器處理方式 編譯器對左方程式的處理順序如下: 第1行第2行第3行第4行第5行第6行第7行第8行第9行第10行第11行第12行
直譯器處理方式 直譯器對左方程式的處理順序如下: 第4行第5行第6行第7行第8行第9行第10行第5行第6行第7行第8行第9行第10行第5行第11行第12行
編譯器及直譯器之區別
程式語言的資料型態 資料型態是指一群個體(object)以及作用在這群個體上的運算 常見的資料型態分類方式是將資料型態分為基本資料型態(elementary data type)及結構性資料型態 (structured data type)二種
基本資料型態 (1/3) 基本資料型態是指不可再切割為更小單位的型態類別 如數值、字元(character)、布林值(boolean value)、列舉式資料型態(enumerated data type)及指標資料型態 (pointer data type)
基本資料型態(2/3) 數值一般可分為整數與浮點數二種 浮點數代表具有小數位數之數值 字元會佔用一個位元組(byte)的記憶體空間 布林值有「真」(true)與「假」(false)二種 通常程式語言不允許布林值與數值混合使用 但C/C++語言卻允許布林值與數值混合使用,在C/C++語言中只要數值非0便會被視為「真」,若數值為0則視為是「假」 BASIC語言以「-1」代表布林值「真」,「假」則為「0」。
基本資料型態(3/3) 列舉式資料型態是指將需要的資料一一定義與列舉出來 指標可以是記憶體位址或nil 假設旗標公司編輯部有5位主編,分別是Alice,Bob,Dick,Mark與Peter,C語言將此份資料定義成一列舉式資料型態之語法如下: enum Editor {Alice, Bob, Dick, Mark, Peter}; 指標可以是記憶體位址或nil 因為指標可以隨意存取記憶體空間中之內容,所以指標型態的彈性很大,但會使得程式的可靠度不佳、存取速度較慢及可能造成懸置引用(dangling reference)的問題 Java語言並未提供指標型態供程式設計師使用
C語言指標的用法 C語言使用「*」做為指標變數的「取值運算子」,「&」做為一般變數的「取址運算子」。 範例如下: int a,*b;
C語言的指標與陣列型態 C語言的指標與陣列型態經常會被比較 C語言的陣列的起始位址有二種不同的表示法,分別是: 陣列名稱 陣列的第一個元素之位址 範例 int xxx[168]; xxx為陣列名稱,C語言陣列元素規定由0開始編號,所以本題陣列元素的編號由0~167,共168個元素。代表陣列的起始位址的表示法有以下二種:xxx及&xxx[0]
使用指標資料型態時常見的錯誤
結構性資料型態 常見的結構性資料型態 字串(string) 記錄(record) 聯合(union)資料型態 由字元(character)組成。例如,”book”、”computer”、”internet”均為字串 記錄(record) 記錄是由固定數目,但型態可以不同的元素所組成 聯合(union)資料型態 同一個變數佔用的記憶體空間,在不同執行時間可由不同型態的值存放
結構性資料型態(cont.) 常見的結構性資料型態 陣列(array) 集合(set): 檔案(file): 陣列由名稱、維度(dimension)、索引(index)及元素型態陣列等元件組成。陣列的主要有二項限制: 元素必須存放在連續的記憶體空間。 元素的型態必須完全相同 集合(set): 集合可表示沒有順序關係的資料,若程式語言提供集合資料型態則應提供聯集、交集及差集運算。 檔案(file): 檔案是由相關的記錄所組成
型態強制轉換(coercion) 型態強制轉換是指將某一型態的值強制轉換成為另一種型態之值 可分為二種不同作法 「寬化」(widening) 將某一型態的值轉換為另一型態,而值不會改變者 「窄化」(narrowing) 將某一型態的值轉換為另一型態,而值可能會改變者
寬化 範例
窄化 範例
多意(polymorphism) 對同一個運算式中的運算子(operator)而言,若運算子會因為運算元(operand)的型態不同,而使得運算子具有不同的意義便稱為「多意」 下表為利用Basic語言舉出的一個「多意」的實例
範例 假設變數A的型態為整數,變數B的型態為浮點數,請問A+B運算式在實際處理時,對於資料型態的處理方式為何? 解:
型態檢驗 型態檢驗是指對運算式中的所有運算元做運算相容性檢驗,通常分為以下二種不同的作法: 靜態型態檢驗(static type checking) 執行前做型態檢驗動作,近代的程式語言多是採用本法。 動態型態檢驗(dynamic type checking) 執行時做型態檢驗動作
型態檢驗範例
外顯式與內隱式「窄化」
變數的定義 變數是由四個元件所組成 名稱(name)、屬性(attribute)、引用(reference)及值(value) 「名稱」即指變數的名字 並非所有的變數都有名稱的,如指標變數(pointer variable)的名稱僅是引用(reference)該變數的方式,並非真正的名稱,此類變數稱為不具名變數(anonymous variable) 「屬性」是指變數的型態 如整數、實數、字元、字串、布林值等 「引用」指變數的儲存位址 「值」指變數的值
變數的實例 以C語言為例 int X = 5; 變數的「名稱」為X 「屬性」為int (即整數) 「值」為5 (就C語言而言,此處的用法為變數「初值設定」,C語言允許在宣告變數時一併設定初值) 對程式設計師而言,一般來說「引用」是比較模糊的一種概念,因為比較沒有具體的東西可代表;就C語言而言,變數的儲存區空間的配置有二種可能性:執行前配置或執行時配置。本範例中變數X所配置的空間有可能是執行前配置或執行時配置,必須視變數所在程式中的位置才可確定儲存區空間何時配置
繫結 繫結(binding)又稱為綁定,是指二種事物相結合的動作 通常會依發生時間點的不同將繫結的動作分為 靜態繫結(static binding) 執行前進行繫結作業 動態繫結(dynamic binding)二類 執行時進行繫結作業
繫結範例-1 近代廣泛被採用的程式語言如C、C++、Java或Basic語言均要求程式設計師要先宣告變數才可在程式中使用該變數,此類語言中變數的名稱與型態的繫結時間是在編譯時(即執行前),因此稱為靜態型態繫結(static type binding) 另一類程式語言如APL語言允許程式設計師可不宣告變數便可在程式中使用該變數,此類語言中變數的名稱與型態的繫結時間是在執行時,因此稱為動態型態繫結(dynamic type binding)
繫結範例-2 儲存區配置法可分為二類,分別是靜態儲存區配置法(static storage allocation)及動態儲存區配置法(dynamic storage allocation) 靜態儲存區配置法是指程式執行前配置記憶體的空間供程式執行時使用,因此變數的名稱與儲存區空間的繫結時間是在執行前 動態儲存區配置法則是指程式執行時配置記憶體的空間供程式執行時使用,因此變數的名稱與儲存區空間的繫結時間是在執行時
完全計算與捷徑計算 完全計算(complete circuit evaluation) 對運算式作求值動作時,必需做完整個運算式才可得出最後的結果,如此的計算方式便稱為完全計算 捷徑計算(short circuit evaluation) 對運算式(通常是指布林運算式)作求值動作時,無需做完整個運算式即可得出最後的結果
捷徑計算實例 X1×X2×X3×……×X100 X1 and X2 and X3 X1 or X2 or X3 若X1為false則運算式的結果為false X1 or X2 or X3 只要X1為true則運算式的結果為true
高階語言的實作– C/C++ C與C++內定採用捷徑計算
高階語言的實作 -- Java Java採用不同的運算子(operator)來代表採用“捷徑計算”或“完全計算” || 及&& 代表採用捷徑計算 | 及& 代表採用完全計算
領域與範圍 領域(scope) 是指一段空間,一個變數的「領域」其實就是變數可被引用(reference)的程式片段的區域 範圍(extend)指系統配置儲存區空間供變數儲存其值的時間區段
變數引用規則 靜態領域法(static scoping) 動態領域法(dynamic scoping) 在區塊(block)中未定義的變數,根據程式的結構,在程式中包含其區塊的上一層區塊處尋找此變數是否在此區塊內定義,若找到變數的宣告則變數即是在此區塊內定義,若未找到則繼續往外層尋找直到變數第一次宣告處為止。若搜尋完整個程式仍未找到變數之宣告,則該變數即為未定義之變數 近代高階語言如C、C++、Java、Pascal及Basic均是採用「靜態領域法」 動態領域法(dynamic scoping) 動態領域法是指在區塊中未定義的變數的型態會依副程式被呼叫的反順序來尋找。
範例
程式設計基本觀念 流程圖 結構化程式設計 程式語言的運算子
流程圖 發展程式時可利用流程圖(flow chart)來做為分析的工具 流程圖主要的作用便是將計算方法轉換為圖形化的方式來表達 採用流程圖的主要優點 可讀性較高 容易維護 容易除錯
常用流程圖符號
判斷課程是否必須重修之過程 所對應的流程圖
結構化程式設計 結構化程式設計是指從事程式設計的過程中,依照程式的邏輯特性將程式細分成幾個較小的問題,再將這些較小的問題同樣依照程式的邏輯特性再往下細分成更小的問題,依此類推直到很容易編寫程式的單元時為止 當採用結構化程式設計法來設計程式時,應當儘量避免使用goto命令,以避免破壞程式的可讀性及結構性
結構化程式設計 (cont.) 主要優點 主要缺點 程式可分工完成 容易除錯 可讀性較高 較容易維護 經由結構化程式設計原則產生的程式碼通常會較大 程式執行時間較久
結構化程式設計 實例--編輯程式(editor)
程式語言的運算子 高階語言常用的運算子(operator)有三類,分別是算術、關係及邏輯運算子 這三類運算子的關係如下表所示:
程式語言的運算子 (cont.) 程式語言對運算子定義運算優先順序的主要目的是希望程式能有唯一的執行結果,若運算子之運算優先順序未定義則可能使得程式執行結果不唯一
BASIC語言運算子執行的優先順序表
範例
程式設計的三大結構 結構化程式設計的基本結構有 循序結構(sequential structure) 選擇結構(selection structure) 反覆結構(iteration structure)
循序結構 循序執行的程式段即為循序結構
重要範例 – 二個變數值交換問題 重要結論:若要將二個變數的值交換,最少應增加一個額外變數
選擇結構 選擇結構可分為 單路選擇結構 雙路選擇結構 多重選擇結構
if (條件) then {條件成立時應執行的敘述} 單路選擇結構 單路選擇結構(single path selection structure) 條件成立時有對應的敘述應被處理,但是當條件不成立時則沒有對應的敘述應被處理,因此稱為單路選擇結構 高階程式語言提供的單路選擇結構,語法如下: if (條件) then {條件成立時應執行的敘述}
單路選擇結構對應的流程圖
雙路選擇結構 雙路選擇結構(double path selection structure) 條件成立時有相對應的敘述必須被處理,而且當條件不成立時也有相對應的敘述要處理,因此稱為雙路選擇結構 高階程式語言提供的雙路選擇結構,語法如下: if (條件) then 「條件成立時應執行的敘述」 else 「條件不成立時應執行的敘述」
雙路選擇結構對應的流程圖
範例
範例
範例
範例 有一流程圖如下,試寫出對應的if敘述 答案為 if (C1){ if (C2) S1;} else S2;
多重選擇結構
C語言的 switch 敘述 C語言的 switch 敘述屬於外顯分歧(explicit branch)結構 若要表達相同的語意,「break」命令是不應省略
C語言的 switch 範例
反覆結構 反覆結構是指重覆執行的敘述群 反覆結構分為二類 前測迴路(pre-test loop) 後測迴路(post-test loop)
前測迴路 前測迴路的運作原則是先判斷執行迴圈敘述的條件是否成立,若「成立」則執行迴圈敘述,若「不成立」則跳離迴圈結構 迴圈執行的最少次數是0次 迴圈執行的最多次數是無限多次
前測迴路流程圖
前測迴路種類 常見的前測迴路可分為二類 「while-loop」 「for-loop」
C/C++/Java 語言的「while-loop」 語法結構如右 若「迴圈敘述」只有一條,語法結構中的「{」及「}」 可以省略,否則「{」及「}」必須被保留下來 以「while-loop」設計寫一個程式片段來完成「s=1+2+…+100」的工作
Pascal語言的「while-loop」 若「迴圈敘述」只有一條,語法結構中的「begin」及「end」 可以省略,否則「begin」及「end」必須被保留下來
Basic語言的「while-loop」
範例
C/C++/Java 語言的「for-loop」 語法結構如右
範例
範例
範例
Pascal語言的for loop
Pascal語言的for loop (cont.)
範例
Basic語言 的 for loop
範例
範例
範例
範例
範例
後測迴路 後測迴路的運作原則是先執行迴圈敘述再判斷繼續執行迴圈敘述的條件是否成立,若「成立」則執行迴圈敘述,若「不成立」則跳離迴圈結構 迴圈執行的最少次數是1次 迴圈執行的最多次數是無限多次
後測迴路的程式流程圖
C/C++/Java語言的後測迴路 語法結構如右 先執行迴圈敘述再檢查<條件>,<條件>成立時執行迴圈敘述,當<條件>不成立時將離開迴圈結構
以do-while loop來完成s=1+2+…+100 此程式段的「條件」為「i <= 100」,先執行迴圈敘述「s=s+i; i=i+1;」再做條件測試,若條件成立則再執行迴圈敘述「s=s+i; i=i+1;」一次,因此在i的值為1~100時迴圈敘述會執行
Pascal語言的後測迴路 語法結構如右 先執行迴圈敘述再檢查<條件>,<條件>不成立時執行迴圈敘述,當<條件>成立時將離開迴圈結構
範例 以for-loop設計程式段 ,此程式段可求得 (1)S的值,S=1+2+3+…+N。 (2)N的階層值,N!=1×2×3×…×N
副程式與參數傳遞 撰寫程式時通常會把某些可完成特定功能的敘述集合在一起,此類被集合在一起的程式段被稱為副程式(subroutine) 副程式可以重複被呼叫
副程式的組成 副程式由四項元素組成 參數分為實際參數及型式參數二類 實際參數(actual parameter)是指呼叫敘述中的參數串列 副程式名稱、參數、執行環境及程式段 參數分為實際參數及型式參數二類 實際參數(actual parameter)是指呼叫敘述中的參數串列 型式參數(formal parameter)則是指被呼叫的副程式中的參數串列
實例 變數a及b為型式參數,可在呼叫程式中利用swap(x, y)來呼叫副程式 變數x及y為實際參數 執行的環境為變數a, b及t
副程式的控制流程轉移 「呼叫程式執行了呼叫副程式的敘述」,這個動作啟動了副程式的執行 當副程式要開始執行時,程式的控制流程(control flow)會由呼叫程式中呼叫副程式的敘述(這個敘述的下一個敘述的位址被稱為被呼叫副程式的返回位址)轉移到副程式的進入點,隨後開始副程式的執行,當副程式執行結束時,會將程式的控制流程轉移到返回位址處所儲存的敘述,繼續程式執行的動作
副程式的控制流程轉移 (cont.) 副程式「被呼叫」、「執行」及「執行結束」時,這三個重要時刻,呼叫程式(A)及被呼叫副程式(B)間,控制流程的轉移方式 如右圖 執行順序為: 「敘述群A1」「呼叫副程式B的敘述」「敘述群B1」「expnext」「敘述群A2」 其中「expnext」代表叫式中呼叫副程式的敘述之下一個敘述,此敘述的位址即為副程式的返回位址
副程式 的種類 副程式可分為程序(procedure)及函式(function)二類 程序與函式最大的不同是程序沒有傳回值而函式會有傳回值。不論是程序或函式皆為副程式,因此均會符合副程式的特性 這是一個利用C語言的語法及「輾轉相除法」求最大公因數的範例,這個程式段利用了遞迴副程式(recursive subroutine)觀念
參數傳遞 近代程式語言使用的參數傳遞法主要有二類,分別是傳值呼叫法(call by value)與傳址呼叫法(call by address)
傳值呼叫法 傳值呼叫法是將呼叫程式中實際參數的值,傳給副程式中對應的型式參數 系統必須配置額外的記憶體空間供型式參數使用
傳址呼叫法 傳址呼叫法則是將呼叫程式中實際參數的位址,傳給副程式中對應的型式參數 系統不須配置額外的記憶體空間供型式參數使用 有一點應特別注意的,雖說是傳遞位址,實際上實際參數的值也會一併隨著位址傳給型式參數使用
範例 當x=3, y=4時,若以swap(x, y)敘述來呼叫副程式swap,請問當副程式執行結束時,變數x及y之值為何?請分別以(1)傳值呼叫法,(2)傳址呼叫法來處理參數傳遞問題
傳值呼叫法解答 系統配置額外的記憶體空間供型式參數a,b存放由實際參數接收到值。變數x、y、a及b與儲存區空間值的對應情形如上圖
傳址呼叫法解答 型式參數a,b將接收到由實際參數x,y傳遞來的位址,換句話說,系統不需配置額外的記憶體空間供型式參數a,b使用。變數x、y、a及b與儲存區空間值的對應情形如上圖 因執行副程式,使得變數a、b之值交換,變數x、y、a及b與儲存區空間值的對應情形如上圖 變數x,y與儲存區空間值的對應情形如上圖
範例 請分別以(1)傳值呼叫法,(2)傳址呼叫法來處理右方程式段之參數傳遞問題,並求得答案
傳值呼叫法解答
傳址呼叫法解答
範例 假設採用靜態領域法且參數傳遞法採用傳值呼叫法,則右方程式執行結果為何?
本題中由於在不同的程式段中,定義了相同符號的變數,為了要方便區分,將採用以下規則來處理變數同名問題:(以變數x為例) 由於在主程式A及副程式C中均定義了變數x,因此 用xA代表在程式段A中定義的變數x 用xC代表在程式段C中定義的變數x
(1)在A中執行 (2)A呼叫C,在C中執行
(3)C呼叫B,在B中執行 (4)由B return回C中執行 (5)由C return回A中執行
考慮右方程式,假設參數傳遞法採用傳址呼叫法,請分別求出使用靜態領域法及動態領域法的執行結果
(1)在MAIN中執行 (2)MAIN呼叫B,在B中執行
(3)B呼叫A,在A中執行
(4)由A return回B,執行ZB=XB敘述 靜態領域法結果如上圖 動態領域法結果如上圖 (5)由B return回MAIN,執行writeln(YMAIN) 靜態領域法結果:20 動態領域法結果:25
範例 請以遞迴副程式觀念來設計程式段,此程式段可求得 (1)S的值,S=1+2+3+…+N。 (2)N的階層值,N!=1×2×3×…×N。