C++ 入門指南

單元 17 - 修正後的數學公式

程式中可能會發生的錯誤有三種,分別是語法錯誤 (syntax error) 、執行期間錯誤 (run-time error) 及語意錯誤 (semantic error)

Syntax Errors
→ compiler or interpreter
Runtime Errors
→ exception handling
Semantic Errors
→ programmer

編譯器 (compiler) 會直接幫我們挑出語法錯誤,例如打錯識別字 (identifier) 名稱或是漏打分號等等。執行期間錯誤的話, C++ 另有例外處理 (exception handling) 的機制,讓我們可以處理丟出例外的狀況。三種錯誤中最麻煩的,就是語意錯誤了,因為有語意錯誤的程式,程式可以順利執行完畢,卻跑出錯誤的結果。

我們的 Encrypt 類別 (class) 目前正是碰到了發生語意錯誤的情況,這是說

y = a * x + b
m = y % n
r = m + diff

其中 ab 若是 09 隨機整數,有些組合成立,可以得到正確結果,有些組合卻會得到錯誤的結果,這是為什麼呢?嗯,好麻煩唷!這樣就得討論好多數學,打斷我們發展程式的腳步,所以我們不打算仔細討論這背後的數學理論,我們繼續測試,直接來找出哪些組合會得到錯誤的結果吧!

要知道哪些組合可能會發生錯誤,我們就得知道 ab 的值,這不難,印出來就看得到了

// 引入標準程式庫中的 cstdlib 及 ctime
#include <cstdlib> // srand(), rand()
#include <ctime>   // time()
// 引入標準程式庫中的 iostream
#include <iostream> // cout, endl

// 引入 Encrypt 類別的標頭檔 
#include "encrypt01.h"

// 使用 std 中的兩個名稱
using std::cout// 標準輸出串流的物件
using std::endl// 新行符號,等於 '\n'

// Encrypt 的建構函數 
Encrypt::Encrypt() {
    // 呼叫 setter 設定 code_array
    set_code_array();
}

// 設定 code_array 的 setter 成員函數
void Encrypt::set_code_array() {
    // 初始化擬隨機數產生器 
    srand(time(0));
    // 取得 a 、 b 值 
    int a = rand() % 10;
    cout << a << ", "// 印出 a 的值
    int b = rand() % 10;
    cout << b << ", "// 印出 b 的值
    
    // 利用公式建立密碼表字串     
    int x, y, m;
    char c = 'a';
    string s;
    int i;
    for (i = 0; i < 26; i++) {
        x = c;
        y = x * a + b;
        m = y % 26;
        s += m + 97;
        c++;
    }
    
    // 將建立好的密碼表直接設定給成員變數
    code_array = s;
}

// 回傳密碼表字串的 getter 成員函數
string Encrypt::get_code_array() {
    return code_array;
}

/* 程式語言教學誌》的範例程式
   https://kaiching.org/
   檔名:encrypt03.cpp
   功能:實作發展中版本的 Encrypt 類別
   作者:張凱慶 */

這裡在 set_code_array() 加入印出 ab 值的程式碼,因為 ab 是屬於 set_code_array() 的區域變數 (local variable) ,因此只能在 set_code_array() 裡使用這兩個變數 (variable) ,記得,因為用到 coutendl ,所以 encrypt03.cpp 要先引進 iostream 然後 using

來編譯測試看看囉

-encrypt0301-

第一次, a8b0 就出問題,再多測試幾次看看

-encrypt0302-

跑了幾次下來,似乎 a 為偶數或 0 就會跑出不符預期的結果,那我們就把 a 改成不是偶數或 0 好了!公式修改如下

if (a % 2) != 0 {
   y = a * x + b
   m = y % n
   r = m + diff
}

這樣我們把發展中的版本 encrypt03.cpp 修改為 encrypt04.cpp ,其中需要修改的部份只有 set_code_array() ,如下

// 引入標準程式庫中的 cstdlib 及 ctime
#include <cstdlib> // srand(), rand()
#include <ctime>   // time()
// 引入標準程式庫中的 iostream
#include <iostream> // cout, endl

// 引入 Encrypt 類別的標頭檔 
#include "encrypt01.h"

// 使用 std 中的兩個名稱
using std::cout// 標準輸出串流的物件
using std::endl// 新行符號,等於 '\n'

// Encrypt 的建構函數 
Encrypt::Encrypt() {
    // 呼叫 setter 設定 code_array
    set_code_array();
}

// 設定 code_array 的 setter 成員函數
void Encrypt::set_code_array() {
    // 初始化擬隨機數產生器 
    srand(time(0));
    
    // 取得 a 、 b 值,其中 a 不等於 0 或偶數 
    int a = 0;
    int b = 0;
    while (a % 2 == 0) {
        // rand() 回傳 0 到 RAND_MAX 之間的擬隨機整數
        a = rand() % 10;
        b = rand() % 10;
    }
    
    // 印出 a 、 b 值
    cout << a << ", ";
    cout << b << ", ";
    
    // 利用公式建立密碼表字串     
    int x, y, m;
    char c = 'a';
    string s;
    int i;
    for (i = 0; i < 26; i++) {
        x = c;
        y = x * a + b;
        m = y % 26;
        s += m + 97;
        c++;
    }
    
    // 將建立好的密碼表直接設定給成員變數
    code_array = s;
}

// 回傳密碼表字串的 getter 成員函數
string Encrypt::get_code_array() {
    return code_array;
}

/* 《程式語言教學誌》的範例程式
   https://kaiching.org/
   檔名:encrypt04.cpp
   功能:實作發展中版本的 Encrypt 類別
   作者:張凱慶 */

重新編譯執行,結果如下

-encrypt04-

我們先把 ab 的初值設為 0 ,然後以迴圈 (loop) 取得隨機的 ab 值,用 a 除以 2 的餘數為 0 當作迴圈的執行條件,因此當 a 等於 0 或偶數時,迴圈就會持續進行,直到 a 不為 0 或偶數為止。肉眼檢查下,似乎只要 a 為奇數,計算出的結果就不會有問題囉!

下面我們要開始實作處理編碼的部分,也就是實作 ToEncode() 成員函數 (member function) 。

中英文術語對照
語法錯誤 syntax error
執行期間錯誤 run-time error
語意錯誤 semantic error
編譯器 compiler
識別字 identifier
例外處理 exception handling
類別 class
區域變數 local variable
變數 variable
迴圈 loop
成員函數 member function
重點整理
1. 程式中可能會發生語法錯誤、執行期間錯誤或語意錯誤,編譯器會直接挑出語法錯誤,而執行期間錯誤可用例外處理機制來防範。
2. 發生語意錯誤的程式可順利執行跑出結果,可是結果不符合預期。
3. Encrypt 的數學公式經過再三的測試檢驗,發現問題出在 a0 或偶數的情況,因此要避免 a0 或偶數。
問題與討論
1. 什麼是語法錯誤?試舉出語法錯誤的五個例子。
2. 什麼是執行期間錯誤?想一想有哪些情況會發生執行期間錯誤?又要怎麼樣防範呢?
3. 為什麼語意錯誤最麻煩呢?
4. 我們是用什麼方式找出數學公式的錯誤呢?
練習
1. 承接上一個單元的猜數字遊戲,將新程式寫在 exercise1701.cpp 中,在遊戲迴圈中用變數 times 計算使用者猜測的次數,並在遊戲結束時印出 times
2. 承上題,將新程式寫在 exercise1702.cpp 中,試著加入一個整數參數 lengthArrayNumber() 裡, length 取代固定的數字 4
3. 承上題,將新程式寫在 exercise1703.cpp 中,將原本的數字 1000 改成用標準程式庫 cmathpow() 計算。

上一頁 單元 16 - 繼續測試
回 C++ 入門指南目錄
下一頁 單元 18 - 編碼
回 C++ 教材
回程式語言教材首頁