本文在中科大軟件學院孟寧老師的指導下完成,意在通過對小型程序的分析來幫助體會軟件工程方法、思想。
本文的參考資料及資源來自:軟件工程——碼農的自我修養。
1. VS Code開發環境配置
1.1 VS Code 安裝
有關VS Code的安裝配置可以參考該資料:https://mp.weixin.qq.com/s/sU9Wh12JZqQyJfEBBafFAQ
在本文中,我們對此進行簡單介紹。我們可以在VS Code官網下載,它是一款免費的輕量級IDE,通過擴展插件可以支持多種語言。
下載地址:https://code.visualstudio.com/#alt-downloads
1.2 C/C++編譯器下載
在VS Code中,當我們通過加載擴展插件來實現支持時,它並不會包含語言的編譯器與調試器。這里所說的編譯器與調試器,對於C++而言,即為MinGw。
C/C++編譯器則為MinGw: http://www.mingw.org/
下載完成並安裝后,我們可以得到以下目錄:
1.3 環境變量配置
通常情況下,在安裝MinGw時,會建議或者要求我們將其添加到環境變量中。但如果我們忘記了配置,也可以通過以下方法手動進行配置。
首先,我們需要獲取到MinGw在我們系統中的安裝位置,如上所示,即為:C:\MinGw 。
之后,右鍵點擊此電腦 -> 選擇屬性 -> 高級系統設置 -> 環境變量,在此處可以管理我們計算機的環境變量。我們找到下方系統變量中的Path選項:
通過編輯Path選項,新建添加新環境變量地址,填入上述安裝位置。需要注意的是,我們此時需填入C:\MinGw\bin。
以上即為C/C++編譯器的環境安裝配置過程。對於其他語言,如Java、Python、Golang,我們均可以采用此種方法。
我們可以通過命令提示符查看MinGw的安裝是否成功,使用gcc -v指令:
1.4 VS Code中配置C/C++開發環境
1.4.1 加載C/C++擴展
在VS Code左側擴展選項中,我們搜索C++即可搜索到插件,點擊Install后即可安裝完成:
1.4.2 task.json與launch.json配置
VS Code通過 task.json 配置文件來獲取編譯程序的方式,通過修改task.json就可以利用VS Code進行gcc編譯源碼了。
一個參考的task.json文件配置如下:
{ "version": "2.0.0", "tasks": [ { "type": "shell", "label": "gcc build active file", "command": "gcc", "args": [ "-g", "${file}", "-o", "${fileDirname}/${fileBasenameNoExtension}"// ], "group": { "kind": "build", "isDefault": true } } ] }
關於task.json的格式,可以參考以下文檔:https://go.microsoft.com/fwlink/?LinkId=733558
VS Code通過launch.json配置文件中的配置來調用調試器對程序進行調試。通過快捷鍵Ctrl+Shift+P,搜索launch.json,可以打開launch.json進行配置。
一個參考的launch.json的配置文件如下:
{ "version": "0.2.0", "configurations": [ { "name": "gcc build and debug active file", "type": "cppdbg", "request": "launch", "program": "${fileDirname}/${fileBasenameNoExtension}", "args": [], "stopAtEntry": true, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "MIMode": "gdb", "setupCommands": [ { "description": "Enable pretty-printing for gdb", "text": "-enable-pretty-printing", "ignoreFailures": true } ], "preLaunchTask": "gcc build active file", "miDebuggerPath": "C:\\MinGW\\bin\\gdb.exe" // 此處為上述的MinGw安裝地址 } ] }
關於launch.json文件的配置,可以參考以下文檔:https://go.microsoft.com/fwlink/?linkid=830387
1.4.3 驗證安裝
可以看到,運行以下示例成功,說明環境配置成功!
2. 代碼中的軟件工程思想
2.1 代碼注釋風格
良好的注釋風格,可以幫助代碼閱讀者、維護者等人員快速明確地了解到代碼所實現的功能等,方便Debug。一個優秀的代碼注釋風格如下,這是孟寧老師所給出的案例,非常值得學習:
從中,可以清楚地看到,這個文件中的代碼的文件名、作者、模塊名、語言類型、運行環境、版本時間以及描述等,對我們理解代碼十分有幫助。
2.2 模塊化設計
2.2.1 模塊化原理
模塊化(Modularity)是指,在指軟件系統設計時保持系統內各部分相對獨立,以便每一個部分可以被獨立地進行設計和開發。這樣使得我們在開發過程中,每一個模塊都執行自己單一的功能目標,可以獨立於其他軟件模塊,這樣也就方便了開發人員之間的配合。
模塊化的基本原理是關注點的分離(Soc,Separation of Concerns),翻譯成中文其實大概便是“分而治之”。
2.2.2 模塊化程度
在實際的軟件設計開發中,我們一般使用耦合度(Coupling)和內聚度(Cohesion)來作為軟件模塊化程度的高低,它們同樣是判斷一個軟件設計優良程度的重要參考。
耦合度,是指一個軟件中各個模塊之間的依賴程度,從高到低在大致上可以分為緊密耦合(Tightly Coupled)、松散耦合(Loosely Coupled)。我們所追求的是松散耦合,它表明我們所設計的軟件各個模塊的依賴程度較低,是比較好的設計。
內聚度,是指一個軟件模塊內部各種元素之間互相依賴的程度。理想的內聚是功能內聚,也即一個模塊只做一件事,只完成一個主要功能。
2.2.3 模塊化設計:源碼分析
在實現模塊化設計時,我們可以將多種數據封裝成結構體、將實現功能封裝成一個文件,可以幫助我們簡化代碼:
在上圖中,我們將數據結構以及函數聲明放在一個linklist.h的頭文件中,其實現放在linklist.c文件,需要調用時再在main函數中去調用,這樣就實現了較低的耦合度,也方便我們作為程序員去debug。
2.3 軟件模塊接口
2.3.1 接口的定義
接口是通信雙方共同遵守的一種規范,在軟件系統內部一般的接口方式是通過一組API函數來約定軟件模塊之間的溝通方式。面向對象與面向過程的語言在對接口的是實現一般不相同。前者的接口是對象對外開放的一組屬性和方法的集合;后者則是數據結構和操作這些數據結構的函數等。
2.3.2 接口的應用
上圖借用了孟寧老師提供的工程源碼文件中的某個源碼文件。在這個文件中,可以看到其聲明了許多接口,這些接口的實現會放在.c文件中。但不通過具體的實現代碼,我們依然可以通過接口的名稱、參數、返回值以及相關描述了解到大概的功能,這些接口的也可以被多次重復使用,提高了代碼的復用性。
2.4 線程安全
2.4.1 線程的基本概念
線程(thread)是操作系統能夠進行運算調度的最小單位。它包含在進程之中,是進程中的實際運作單位。一個線程指的是進程中一個單一順序的控制流,一個進程中可以並發多個線程,每條線程並行執行不同的任務。
2.4.2 可重入與線程安全
線程安全問題都是由全局變量及靜態變量引起的。若每個線程中對全局變量、靜態變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執行讀寫操作,一般都需要考慮線程同步,否則就可能影響線程安全。
可重入(reentrant)函數可以被多個線程並發執行,而通常不會導致由於共享數據而導致結果錯誤;不可重入函數則在多個線程並發執行時,如果不能保持線程互斥,那么就會導致結果的錯誤。對於軟件工程而言,我們應該以比較悲觀的方式去評估函數:即可重入函數不一定是線程安全的,可能是線程安全的函數就不是線程安全的函數,不可重入函數一定不是線程安全的函數。
2.4.3 線程安全:代碼分析
首先,對於線程安全,我們所關注的焦點一般在以下幾個方面:
i. 所有的函數是不是都是可重入函數:分析函數有沒有訪問臨界資源,若有則必須仔細分析其互斥的處理過程;
ii. 不同的可重入函數有沒有可能同時進入臨界區:讀寫互斥應該如何去考慮;
下面,同樣地,通過孟寧老師所給出的代碼,我們選擇一段代碼來加以解釋:
在需要進入臨界區時,我們對代碼進行了加鎖操作以確保其他函數無法使用臨界區的資源,當我們退出臨界區后及時解鎖供以其他函數訪問,最后銷毀互斥鎖,這就是一個典型的線程安全代碼。
3. 總結
孟寧老師於課堂上講解的各個知識點,使我對軟件工程中的開發思想、方法有了更深一步地理解。同時,經過這篇博客的書寫,我對這些知識的理解更上一層樓了,更為重要的是,動手能力也有所提高,在此深表感謝。
最后,再次強調,參考資料於此:軟件工程——碼農的自我修養