學習筆記:電腦科學 (9645) - 軟體與系統基礎

你好!歡迎來到電腦科學的基礎:軟體。在這一章中,我們將跨越硬體(實體元件),深入探討賦予電腦生命的指令與邏輯。無論你是正在編寫第一個程式,還是想了解作業系統運作背後的原理,理解軟體(包括其設計、建構與執行方式)都是至關重要的。我們將涵蓋程式設計中使用的基本資料型態,以及管理電腦記憶體並執行應用程式的複雜程序。讓我們開始吧!

1. 硬體與軟體之間的關係 (3.6.1)

電腦系統完全依賴其硬體組件與軟體指令之間的交互作用。

  • 硬體 (Hardware): 電腦系統中實體的電子組件(例如:CPU、RAM、硬碟)。
  • 軟體 (Software): 透過硬體執行的指令序列。沒有軟體,硬體不過是一堆電路板而已!

1.1 軟體的分類 (3.6.2)

軟體大致可分為兩大類:

系統軟體 (System Software)

此類軟體負責管理與控制電腦硬體,讓應用軟體得以執行。它在使用者/應用程式與實體機器之間扮演中介角色。

最重要的系統軟體是作業系統 (Operating System, OS)

  • 隱藏複雜性 (Hiding Complexity): 作業系統的核心職責之一,是將硬體處理的複雜細節從使用者和其他軟體中隱藏起來。
  • 公用程式 (Utility Programs): 這些程式增加了額外的功能以輔助系統管理。(例如:病毒檢測程式、壓縮軟體、磁碟重組工具)。
  • 函式庫 (Libraries): 預先編寫好的函數或子程式集合,可供其他程式使用。
  • 轉譯器 (Translators): 將原始碼轉換為機器碼(處理器可執行的代碼)的程式(如編譯器或直譯器)。

作業系統的主要功能:

  1. 排程 (Scheduling): 決定下一個要執行哪些工作或程序以及執行時間,以有效地管理 CPU。
  2. 記憶體分配 (Memory Allocation): 管理主記憶體 (RAM),確保不同的程式擁有所需的空間且互不干擾。
  3. I/O 裝置管理 (I/O Device Management): 處理輸入與輸出裝置(如印表機、鍵盤、螢幕)。
  4. 中斷處理 (Interrupt Handling): 對來自硬體或軟體需要即時關注的訊號(中斷)進行回應。
應用軟體 (Application Software)

這是為使用者執行特定任務而設計的軟體。(例如:文書處理器、網頁瀏覽器、遊戲、試算表。)

快速重點: 系統軟體管理電腦;應用軟體則為使用者執行任務。

2. 程式設計基礎:資料與指令 (3.1.1, 3.1.2)

當我們編寫軟體時,我們是在使用指令來操縱資料。在操縱資料之前,我們必須先定義資料的型態。

2.1 資料型態 (3.1.1)

資料型態 (Data type) 告訴電腦為該資料分配多少記憶體,以及可以對其執行何種運算。

  • 整數 (Integer): 整數(正數、負數或零)。(例如:5, -100)
  • 實數 / 浮點數 (Real / Float): 帶有分數部分的數字(小數點)。(例如:3.14, 0.5)
  • 布林值 (Boolean): 僅能為 True(真)False(假) 的邏輯值。
  • 字元 (Character): 單一字母、數字、標點符號或符號。(例如:'A', '5', '?')
  • 字串 (String): 字元的序列。(例如:"Hello World")
  • 日期/時間 (Date/Time): 代表特定日期及/或時間的值。

記憶小撇步: 將資料型態想像成容器。你不會用麻布袋(字串容器)來裝水,你需要一個水壺(例如浮點數容器)!

2.2 變數與識別符 (3.1.2)

  • 變數宣告 (Variable Declaration): 預留記憶體空間並為其命名(識別符)。
  • 賦值 (Assignment): 將值放入該變數中。
  • 有意義的識別符名稱: 使用能清楚指示變數用途的名稱至關重要。這會讓程式碼更易於閱讀與維護。(例如:使用 TotalScore 而非 x)。

2.3 輸入、輸出與註解 (3.1.2)

程式必須能夠與使用者互動(輸入)並顯示結果(輸出)。

  • 輸入 (Input): 從使用者獲取資料(例如:詢問年齡)。
  • 輸出 (Output): 顯示結果(例如:列印 "你的最終分數是 95")。
  • 註解 (Comments): 加入程式碼中的解釋性說明。轉譯器會忽略這些內容,但對於人類工程師理解程式運作原理及決策原因而言,它們至關重要。

快速重點: 資料型態定義資料;變數儲存資料;識別符使其具可讀性;I/O 處理使用者互動。

3. 控制流程:邏輯與運算 (3.1.2.1-3.1.2.3)

控制流程語句決定了指令執行的順序。

3.1 算術運算 (3.1.2.1)

這些是標準的數學運算:

  • 加 (+)、減 (-)、乘 (*)。
  • 實數/浮點除法: 結果為浮點數的標準除法(例如:7 / 2 = 3.5)。
  • 整數除法 (DIV): 結果僅保留整數部分的除法(商)。(例如:7 DIV 2 = 3)。
  • 餘數 (MOD): 回傳整數除法後的餘數。(例如:7 MOD 2 = 1)。
  • 乘冪 (Exponentiation): 將數字提升至某個次方。

我們還有調整數值結果的函數:

  • 四捨五入 (Rounding): 將數字調整為最接近的整數(或指定小數位數)。
  • 截斷 (Truncation): 直接砍掉數字的小數部分(總是向零的方向移動)。

3.2 控制結構:選擇與迭代 (3.1.2)

選擇 (Selection)(決策): 允許程式根據條件選擇不同的路徑(例如:IF, ELIF, ELSE, CASE 語句)。

迭代 (Iteration)(迴圈): 重複執行一段程式碼。

  • 確定迭代 (Definite Iteration): 執行固定、預先設定次數的迴圈(例如:FOR 迴圈)。
  • 不確定迭代 (Indefinite Iteration): 直到滿足特定條件才停止的迴圈(例如:WHILE 或 REPEAT UNTIL 迴圈)。
  • 巢狀結構 (Nested Structures): 將一個選擇或迭代結構完全放在另一個結構之內。這在處理二維資料(如網格中的列與行)時很常見。

3.3 關係與布林運算 (3.1.2.2, 3.1.2.3)

關係運算 (Relational Operations) 用於比較兩個值,並產生 布林值(True/False):

  • 等於 (= 或 ==)
  • 不等於 (\( \neq \) 或 !=)
  • 小於 (<), 大於 (>)
  • 小於等於 (\( \le \)), 大於等於 (\( \ge \))

布林運算 (Boolean Operations)(邏輯運算子) 用於結合布林值:

  • NOT: 反轉真值(True 變為 False)。
  • AND: 當且僅當兩個條件皆為 True 時,結果才為 True。
  • OR: 只要至少有一個條件為 True,結果即為 True。
  • XOR(互斥或): 當且僅當只有一個條件為 True 時(兩者不同),結果才為 True。

運算優先順序: 當一個語句有多個布林運算子時,它們會依照特定順序評估:

NOT (最高) > AND > OR (最低)。(括號永遠優先於此順序)。

快速重點: 程式使用運算與控制結構(選擇/迭代)來決定下一步採取什麼步驟。

4. 模組化與子程式 (3.1.2.7)

大型程式過於複雜,無法一次寫完。我們將其分解成更小、更易於管理的區塊,稱為子程式 (Subroutines)(有時稱為函數或程序)。

4.1 子程式與參數

  • 定義: 子程式是一個有名稱的、「獨立」的程式碼區塊,用以執行特定任務。
  • 呼叫/執行: 只需在程式語句中寫下其名稱即可執行(呼叫)。
  • 優點:
    • 模組化: 將問題拆解(分解)。
    • 可重用性: 程式碼區塊可在整個程式中重複使用,無需重複編寫。
    • 易於除錯: 較小的區塊更容易測試與修正。
  • 回傳值: 子程式可以計算結果並將該值回傳給呼叫它的主程式。
  • 參數 (Parameters): 呼叫時傳入子程式的資料,使其能針對特定數值進行運算。

4.2 變數範圍 (區域 vs. 全域)

變數的範圍 (Scope) 定義了程式中可以存取該變數的部分。

  • 全域變數 (Global Variables): 在整個程式中皆可存取的變數。
  • 區域變數 (Local Variables): 在子程式內部宣告的變數。
    • 它們僅在子程式執行期間存在
    • 它們僅在該子程式內部可被存取

良好習慣: 盡可能限制變數範圍(優先使用區域變數而非全域變數)是最佳實踐,因為這能防止其他程式部分意外修改到重要資料。

4.3 堆疊框架 (Stack Frame)

當子程式被呼叫時,電腦需要一種方法來保存目前位置並管理該次呼叫的資料。它會使用一塊稱為堆疊 (Stack) 的記憶體區域。每個子程式呼叫都會建立一個堆疊框架來儲存:

  • 返回位址 (Return Address): 子程式結束後應返回的指令記憶體位置。
  • 參數: 傳入子程式的值。
  • 區域變數: 在子程式內部宣告的變數。

快速重點: 子程式透過區域/全域範圍管理資料存取並提升程式品質,而堆疊框架則處理執行期間的記憶體管理。

5. 資料結構 (3.2, 3.2.1-3.2.4)

資料結構 (Data structure) 是一種在電腦中組織與儲存資料的方式,以便能高效地存取與修改。

5.1 靜態與動態結構

  • 靜態資料結構 (Static Data Structure): 大小固定,在程式編譯或初次執行時決定。(例如:陣列)
    • 優點: 存取速度快;記憶體分配較簡單。
    • 缺點: 若未填滿則浪費空間;若需要更多空間則無法擴展。
  • 動態資料結構 (Dynamic Data Structure): 大小在程式執行期間可變(可增長或縮小)。(例如:串列、佇列、使用連結串列實作的堆疊)
    • 優點: 有效利用記憶體;能處理不可預測數量的資料。
    • 缺點: 記憶體管理較複雜;存取時間有時較慢。

5.2 陣列、串列與記錄 (3.2.1, 3.2.2)

  • 陣列/串列 (Arrays/Lists): 在單一識別符下儲存相同資料型態的一組項目。我們可以使用二維陣列(陣列的陣列,如網格)來儲存複雜資料,例如棋盤或試算表。
  • 記錄 (Records): 相關資料值(稱為欄位/Fields)的集合,這些值可能屬於不同的資料型態,並被分組為單一實體進行操作。

    範例:一個 '學生記錄' 可能包含以下欄位:姓名 (字串)、學號 (整數)、成績 (字元)。

5.3 堆疊 (LIFO) (3.2.4)

堆疊 (Stack) 是一種動態資料結構,最後加入的項目最先被移除。這稱為後進先出 (Last-In, First-Out, LIFO)

類比:一疊盤子——你總是拿走最上面(最新)的那一個。

堆疊運算:

  • Push (推入): 將項目加入堆疊頂端。
  • Pop (彈出): 移除並回傳頂端的項目。
  • Peek (或 Top) (查看): 回傳頂端項目的值而不移除它

堆疊對於管理子程式呼叫(前面討論的堆疊框架)以及檢查運算式中括號的平衡至關重要。

實作: 堆疊通常使用一維陣列實作,並需檢查堆疊是否為空 (Empty)滿 (Full)

5.4 佇列 (FIFO) (3.2.3)

佇列 (Queue) 是一種動態資料結構,最先加入的項目最先被移除。這稱為先進先出 (First-In, First-Out, FIFO)

類比:商店排隊的人——先到達的人先獲得服務。

佇列運算:

  • Enqueue (加入): 將項目加入佇列後端。
  • Dequeue (移出): 移除並回傳佇列前端的項目。

佇列在作業系統中用於列印排程和處理資料緩衝區等任務。

實作:

  • 線性佇列 (Linear Queue): 使用陣列的簡單實作。問題:項目移出後,「前端」會往前移動,導致陣列開頭留下未使用的空間。
  • 環狀佇列 (Circular Queue): 透過將佇列索引繞回陣列開頭,解決了線性佇列的問題。這允許項目移出後能有效重複使用空間。

快速重點: 堆疊是 LIFO(後進先出),而佇列是 FIFO(先進先出)。

6. 軟體設計與開發 (3.3)

軟體開發遵循邏輯結構,以確保最終產品符合需求且易於維護。

6.1 結構化程式設計方法 (3.3.1)

結構化方法側重於清晰度、品質與控制。關鍵特性包括:

  • 模組化程式設計: 將程式拆解為獨立的子程式/模組(如第 4 節所述)。
  • 使用參數與回傳值: 為模組定義明確的輸入與輸出。
  • 使用區域變數: 限制範圍以防止非預期的副作用。

開發工具如階層圖 (Hierarchy Charts)(顯示整體程式結構)與結構圖 (Structure Charts)(顯示模組、參數與資料流)會被使用。

6.2 抽象化與分解 (3.3.2)

  • 分解 (Decomposition): 將大型、複雜的問題拆解為更小、可識別的子問題。這通常透過子程式來達成。
  • 抽象化 (Abstraction): 從問題中移除不必要的細節,專注於解決問題所需的關鍵特徵。(例如:在設計賽車模擬程式時,你會抽象掉引擎螺絲的尺寸,只專注於速度、燃料與位置。)

6.3 軟體開發週期 (3.3.4)

軟體通常透過多個階段建立。這可能是一個迭代(重複)過程,常採用原型法或敏捷開發。

階段 1:分析 (Analysis)

清晰定義問題。透過與目標使用者互動來建立系統的需求。這會產生功能規格說明書與資料模型。

階段 2:設計 (Design)

在編寫任何程式碼之前規劃解決方案。這包括:

  • 設計資料結構(如陣列與記錄)。
  • 設計演算法(使用虛擬碼或流程圖)。
  • 設計模組結構(子程式)。
  • 設計人機介面 (HUI)。

重點可能首先放在關鍵路徑 (Critical Path)——其他所有部分都依賴的解決方案核心。

階段 3:實作 (Implementation)

將模型與演算法翻譯成電腦可以處理的程式碼(指令)。

階段 4:測試 (Testing)

檢查實作是否存在錯誤。必須使用精選的測試資料:

  • 正常資料 (Normal Data): 在一般情況下預期的有效資料。
  • 邊界資料 (Boundary Data): 處於可接受輸入限制邊緣的有效資料(例如,若範圍為 1 到 100,則測試 1 或 100)。
  • 錯誤資料 (Erroneous Data): 系統應拒絕的無效資料(例如,預期輸入數字時卻輸入字母)。

階段 5:評估 (Evaluation)

依據預定標準評判完成的系統:

  • 正確性: 系統是否符合需求?(它解決了問題嗎?)
  • 效率: 它的執行速度有多快(時間),以及佔用多少記憶體(空間)?
  • 可維護性: 程式設計師後續修正、更新或修改程式碼的難易度如何?

快速重點: 優質軟體是透過結構化方法建構的,著重於分解,並遵循分析、設計、實作、測試、評估的週期。

7. 程式語言與轉譯 (3.6.3)

電腦只懂機器碼(二進位),但人類使用高階語言編寫軟體。轉譯器則縮短了兩者之間的差距。

7.1 低階語言

這些語言非常接近硬體,且針對特定處理器的指令集。

  • 機器碼 (Machine Code): 可由 CPU 直接執行的二進位指令(0 與 1)。這是電腦唯一真正理解的語言。
  • 組合語言 (Assembly Language): 使用助憶碼 (Mnemonics)(如 ADD, LOAD 等簡短易讀的縮寫)來表示機器碼指令。

低階語言 (L-L-L) 的優點:

  • 程式設計師對硬體與記憶體有精確的控制。
  • 產生非常快速且高效率的程式碼。

低階語言 (L-L-L) 的缺點:

  • 編寫困難且耗時。
  • 不具可攜性(為一種 CPU 編寫的程式碼無法在另一種上執行)。

7.2 高階語言 (HLL)

這些語言更接近人類語言,使用上更簡單。

  • 命令式高階語言: 指令明確描述執行任務時應遵循的「流程」(步驟序列)的語言。(最常見的語言如 Python, Java, C++ 皆為命令式。)

高階語言 (HLL) 的優點:

  • 易於閱讀、編寫與除錯。
  • 在不同硬體平台上具有可攜性。

7.3 程式轉譯器的類型 (3.6.3.2)

轉譯器將原始碼(人類可讀)轉換為目的碼(機器碼/可執行檔)。

轉譯器 角色與流程 何時使用
組譯器 (Assembler) 將組合語言轉換為機器碼。 當需要控制特定硬體組件時。
編譯器 (Compiler) 在執行之前整段原始碼轉換為機器碼。建立可執行檔。 當需要最終的、快速且安全的產品時(例如:商業軟體)。
直譯器 (Interpreter) 在執行期間逐行進行轉譯與執行。不會產生獨立的可執行檔。 在開發與測試期間,或運行於以可攜性/快速變更為主的系統時。

中間語言 (如 Bytecode): 部分編譯器產生中間語言而非直接的機器碼。

  • 為何使用? 它們比機器碼更具可攜性,並允許在執行前進行安全性檢查。
  • 如何使用? 它們由虛擬機器(直譯該中間代碼)或即時編譯器 (JIT Compiler)(在執行前立即將其轉為機器碼)執行。

快速重點: 程式設計師為了易用性與可攜性使用 HLL,並依賴編譯器(追求速度)與直譯器(追求靈活性)來生成最終的機器碼。

8. 物件導向與函數式範式

儘管大多數 AS 課程重點在於命令式/程序式方法,但現代軟體使用不同的方法,稱為範式 (Paradigms)

8.1 物件導向程式設計 (OOP) (3.9)

OOP 的使用是因為它能更好地建模現實世界的實體,並推動可重用性與安全性。

OOP 核心概念:

  • 類別 (Class): 定義物件共同特性(屬性/Attributes)與行為(方法/Methods)的藍圖或模板。(例如:「汽車」類別。)
  • 物件 (Object): 類別的實例。物件透過建構子 (Constructor)(初始化物件狀態)進行實例化 (Instantiation) 而建立。(例如:「我的豐田車」是「汽車」類別的一個物件。)
  • 封裝 (Encapsulation): 將類別運作的內部方式及其表示的資料與其他類別隱藏起來。這能保護資料的完整性。存取通常透過 GetterSetter 方法來控制。
  • 繼承 (Inheritance): 一種關係,其中新類別(子類別/Subclass 或衍生類別)是現有類別(基底類別/Base Class 或父類別)的更專業版本。(例如:「卡車」子類別繼承了「車輛」基底類別的屬性。)
  • 覆寫 (Overriding): 當從基底類別繼承的方法在子類別中被重新定義,以使子類別有不同的行為。
  • 關聯 (Association): 兩個物件之間較弱的關係,其中一個物件單純地使用另一個物件。(例如:「球隊」物件使用「球員」物件。)

8.2 函數式程式設計 (FP) (3.12)

FP 專注於使用數學函數計算結果,將函數視為一等公民 (First-class objects)(意味著函數可以像參數一樣傳遞,或從其他函數回傳)。

  • 函數應用 (Function Application): 將特定的輸入(參數)給予函數以獲得結果。
  • 函數組合 (Composition of Functions): 結合兩個函數以得到新函數。(若先執行函數 f,再將結果傳給函數 g,這就是 g of f。)
  • 高階函數 (Higher-Order Functions): 以另一個函數為參數、回傳函數作為結果,或兩者皆有的函數。
    • Map: 將函數應用於串列中的每個元素,回傳一個包含結果的新串列。
    • Filter: 處理串列以產生一個僅包含符合特定條件元素的新串列。
    • Fold (Reduce): 透過重複應用組合函數,將串列中的值縮減為單一值。(注意:foldl 從左至右運算,foldr 從右至左運算)。

快速重點: OOP 使用類別與物件來建模現實世界;FP 將函數視為資料,並使用高階函數來高效地操作串列。

9. 其他程式設計工具

9.1 字串處理與轉換 (3.1.2.4)

字串是字元的序列,各種運算允許我們對其進行操作:

  • 長度 (Length): 計算字元數量。
  • 位置 (Position): 找出字元或子字串開始的位置。
  • 子字串 (Substring): 提取字串的一部分。
  • 串接 (Concatenation): 將兩個字串合併在一起。
  • 轉換 (Conversion): 在資料型態之間轉換(例如:將 字串 轉為 整數,或將 字元 轉為其數值代碼)。

9.2 例外處理 (3.1.2.6)

這是一種管理程式執行期間發生非預期事件(例外/Exceptions)的程式設計技術,例如試圖除以零,或試圖開啟不存在的檔案。

如果這看起來有點難,別擔心!例外處理僅僅是為「出錯」時預做規劃,確保你的程式不會當機。

快速回顧: 軟體是硬體執行的指令。它是使用模組化結構(子程式)、定義好的資料型態與邏輯控制流程(選擇與迭代)所建構的。設計過程確保了正確性、效率與可維護性。