來源
此程式是一位同學bkali 的大一下期末大作業。專案名稱為《學校計算機機房管理系統》,採用C++編寫。
執行環境與結果
執行環境
Visual Studio 2022
執行結果
主函式程式碼
int main()
{
// 設定透過標記為-1 會進入登陸介面
int pass = -1;//這裡1方便除錯
while (true)
{
switch (pass)
{
case -1:
pass = loginmenu(pass);
break;
case 0:
return 0;
// 當管理員登陸透過->pass=1 以管理員身份進入系統
case 1:
pass = menu(pass);
break;
}
system("cls");
}
return 0;
}
教室建立相關的程式碼
/*初始化教室*/
void Initialize_Classroom(CLASSROOM a)
{
ofstream CLASSROOM("data/" + a.get_id() + ".txt");
CLASSROOM << a;
ofstream CLASSROOMID("data/classroomid.txt", ios::app);
CLASSROOMID << a.get_id() << endl;
}
主要問題與改進方式
輸入校驗
問題現象
我在使用的過程發現,該程式並沒有設定輸入校驗。以使用者的視角來看,對於錯誤的輸入是很常見的現象,比如需要輸入一個int型別的數字,卻多打了一個a這樣的字元型別。然而該系統並沒有對此類錯誤資訊做出相應的判斷,因此在系統讀取到錯誤的資訊後往往會導致程式執行出錯,進而反饋到使用者的使用上來說就是系統的崩潰。
(在開始選單的選項中輸入‘a’後 程式直接崩潰退出)
改進方式
對於此類問題,則需要在程式讀取到使用者的輸入後,對輸入的資料進行進一步的判斷,如果資料型別是符合的,則可以正常讀取並繼續執行;資料不符合的話就不能讀取,以防止程式崩潰。在此應該清除讀取到的資料並清理快取,並提示出錯的資訊。
改進後的程式碼
bool Isdigit(int c)
{
if (cin.rdstate()) {
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
return false;
}
return true;
}
測試截圖
資源釋放
問題現象
程式有個功能可以查詢相應教室的排課資訊,在查詢前需要先選擇教室。但是在我第二次使用程式時,我發現我不需要選擇教室就能夠直接看到教室資訊,此時顯示的是上一次執行所選擇的教室。
改進方式
在仔細研究程式碼後,發現程式在執行選擇教室時會建立一個臨時檔案來儲存選擇的教室,但是在執行結束後並沒有對此檔案進行刪除、釋放資源。因此我透過atexit()函式來註冊程式正常終止時要被呼叫的函式,即對資源進行釋放。在函式中先判斷臨時檔案是否存在來選擇是否刪除相應的檔案。
改進後的程式碼
#include "menu.h"
#include <cstdio>
using namespace std;
void my_release() {
const char* file_path = "data/classroomtmp.txt"; // 指定檔案路徑
ifstream f(file_path);
if (f.good()) { //判斷檔案是否存在
f.close();
if (remove(file_path) != 0) { // 嘗試刪除檔案
cout << "Failed to delete file." << endl;
}
else {
cout << "File deleted successfully." << endl;
}
}
else {
cout << "File Not Find" << endl;
}
}
int main()
{
//註冊程式結束所呼叫的函式
atexit(my_release);
// 設定透過標記為-1 會進入登陸介面
int pass = -1;//這裡1方便除錯
while (true)
{
switch (pass)
{
case -1:
pass = loginmenu(pass);
break;
case 0:
return 0;
// 當管理員登陸透過->pass=1 以管理員身份進入系統
case 1:
pass = menu(pass);
break;
}
system("cls");
}
return 0;
}
測試截圖
總結
在我對該程式進行逆向開發的過程中,最耗時間的是對於程式的解構和分析,理解每個功能函式以及其中涉及的相互關係。需要對每個程式碼塊進行解讀,理解其原理,這樣在後續發現問題時才能及時找到問題出錯的原因和對應的功能程式碼。其次就是對於程式碼的修改,尤其是高耦合度的程式碼,修改其中的一小部分往往便會造成大範圍的變動,這顯然不是我想要的。因此我採用“打補丁”的方式,對於使用者輸入問題,我並沒有修改讀取的型別,而是進行輸入校驗,以保證在原程式正常執行的情況下修改相應的bug。