陳 哲,王 沖,黃志球
1(南京航空航天大學(xué) 計(jì)算機(jī)科學(xué)與技術(shù)學(xué)院,南京 211106)
2(高安全系統(tǒng)的軟件開發(fā)與驗(yàn)證技術(shù)工業(yè)和信息化部重點(diǎn)實(shí)驗(yàn)室,南京 211106)
程序設(shè)計(jì)是計(jì)算機(jī)、軟件工程等工科專業(yè)的第一門核心必修專業(yè)課程,其教學(xué)內(nèi)容通常包括C 程序設(shè)計(jì)語言[1]、面向?qū)ο蟪绦蛟O(shè)計(jì)語言(例如C++)[2].由于后續(xù)專業(yè)課程(例如數(shù)據(jù)結(jié)構(gòu)、編譯原理、軟件工程、軟件測試等)都需要使用程序設(shè)計(jì)來完成實(shí)驗(yàn)和課程設(shè)計(jì),而且程序設(shè)計(jì)也是計(jì)算機(jī)和軟件行業(yè)招聘面試的核心考查內(nèi)容,所以熟練地掌握程序設(shè)計(jì)技能不僅是學(xué)習(xí)后續(xù)專業(yè)課程的基礎(chǔ),也是獲得理想工作機(jī)會(huì)的重要條件.因此,如何改進(jìn)程序設(shè)計(jì)課程教學(xué)、讓學(xué)生更好地掌握課程知識(shí)點(diǎn)是重要的研究課題[3–7].
筆者在教學(xué)實(shí)踐中發(fā)現(xiàn),雖然采用了網(wǎng)絡(luò)教學(xué)組織[3,4]和在線判題[5–7]教學(xué)系統(tǒng),學(xué)生還是很難掌握程序設(shè)計(jì)語言中一些復(fù)雜的或抽象的理論知識(shí),也很難將這些知識(shí)正確地運(yùn)用到編程實(shí)踐中.以C 程序設(shè)計(jì)教學(xué)為例,指針和內(nèi)存操作較為復(fù)雜[1].程序內(nèi)存被劃分為代碼段、數(shù)據(jù)段、BSS 段、棧、堆等不同的段,而每個(gè)段都有不同的訪問規(guī)則[8],例如:代碼段中的數(shù)據(jù)只能讀不能寫,棧中的數(shù)據(jù)不能被顯式釋放,堆中分配的內(nèi)存塊被釋放后不能再次訪問,對(duì)內(nèi)存塊的訪問不能超過該內(nèi)存塊的上界和下界等.所以照本宣科式地講授相關(guān)的理論知識(shí),學(xué)生難以消化吸收,更無法正確運(yùn)用,使得編寫的程序經(jīng)常帶有內(nèi)存錯(cuò)誤,導(dǎo)致錯(cuò)誤的運(yùn)行結(jié)果和耗時(shí)的調(diào)試過程.事實(shí)上,在工業(yè)界的軟件開發(fā)實(shí)踐中,內(nèi)存錯(cuò)誤也一直是最常見、危害最大的程序錯(cuò)誤之一,它可以導(dǎo)致數(shù)據(jù)腐敗、安全漏洞和程序崩潰等致命后果,例如阿麗亞娜5 號(hào)火箭控制系統(tǒng)軟件的內(nèi)存溢出錯(cuò)誤導(dǎo)致火箭在升空后爆炸[9].因此,教師有必要使用教學(xué)系統(tǒng)幫助學(xué)生牢固掌握指針和內(nèi)存操作的相關(guān)知識(shí)、更快地找到程序中的內(nèi)存錯(cuò)誤,從而正確運(yùn)用指針和內(nèi)存操作.
以面向?qū)ο蟪绦蛟O(shè)計(jì)教學(xué)為例,封裝、繼承、多態(tài)是面向?qū)ο蟮娜筇匦訹2],但是這些概念較為抽象.注意,C 程序設(shè)計(jì)教學(xué)關(guān)注的是程序具體功能相關(guān)的知識(shí)點(diǎn),如循環(huán)語句、函數(shù)等.與之不同,面向?qū)ο蟪绦蛟O(shè)計(jì)教學(xué)關(guān)注的是非功能的知識(shí)點(diǎn),例如封裝、繼承、多態(tài)的主要目標(biāo)是改善大規(guī)模代碼的模塊化、可維護(hù)性、可復(fù)用性、健壯性等.所以咬文嚼字式地講授相關(guān)的概念定義,或者通過小例子輔助教學(xué),學(xué)生都難以理解這些特性的優(yōu)點(diǎn),更無法主動(dòng)地在編程實(shí)踐中合理地運(yùn)用,使得編寫的程序經(jīng)常不具備面向?qū)ο蟮奶卣?導(dǎo)致程序模塊性差、難于維護(hù).因此,教師有必要使用教學(xué)系統(tǒng)幫助學(xué)生深入理解面向?qū)ο蟮奶匦?從而正確運(yùn)用面向?qū)ο蟪绦蛟O(shè)計(jì)思想.
基于上述教學(xué)需求,本文設(shè)計(jì)并使用C++程序設(shè)計(jì)語言實(shí)現(xiàn)了面向程序設(shè)計(jì)課程的教學(xué)系統(tǒng):C 程序內(nèi)存安全性動(dòng)態(tài)分析系統(tǒng)(以下稱為Movec).該系統(tǒng)包含了C 程序編譯器前端,通過遞歸式遍歷和重寫程序源代碼的抽象語法樹(AST),實(shí)現(xiàn)對(duì)程序源代碼的自動(dòng)插樁;通過編譯運(yùn)行插樁后的源代碼,實(shí)現(xiàn)對(duì)內(nèi)存錯(cuò)誤的動(dòng)態(tài)檢測.該系統(tǒng)綜合運(yùn)用了C 程序設(shè)計(jì)、面向?qū)ο蟪绦蛟O(shè)計(jì)(C++)、數(shù)據(jù)結(jié)構(gòu)、編譯原理、軟件工程、軟件測試等計(jì)算機(jī)和軟件工程專業(yè)核心課程的知識(shí),同時(shí)可以展現(xiàn)這些知識(shí)之間的深度融合.學(xué)生通過學(xué)習(xí)和使用該系統(tǒng)有助于理解和掌握相關(guān)的理論知識(shí),以及這些理論知識(shí)在實(shí)際軟件開發(fā)過程中的應(yīng)用,可以激發(fā)學(xué)生學(xué)習(xí)的主動(dòng)性和鉆研的興趣,從而有效地提高教學(xué)效果.
程序分析技術(shù)是一種對(duì)程序行為進(jìn)行自動(dòng)分析的過程,通常用于發(fā)現(xiàn)程序中的缺陷和錯(cuò)誤,例如內(nèi)存安全性缺陷、死循環(huán)等邏輯錯(cuò)誤.
程序分析技術(shù)分為靜態(tài)和動(dòng)態(tài)兩種.靜態(tài)分析不需要運(yùn)行程序,而是通過對(duì)程序代碼進(jìn)行邏輯推理來檢測錯(cuò)誤.由于靜態(tài)分析需要確保性能(在短時(shí)間內(nèi)報(bào)告結(jié)果),而這是以檢測精度為代價(jià)的,因此通常有誤報(bào)和漏報(bào).動(dòng)態(tài)分析通過代碼插樁技術(shù)在代碼中插入用于錯(cuò)誤檢測的代碼片段,從而在運(yùn)行時(shí)實(shí)現(xiàn)對(duì)程序執(zhí)行過程的分析和錯(cuò)誤檢測[10].動(dòng)態(tài)分析的優(yōu)點(diǎn)是考慮了程序?qū)嶋H運(yùn)行環(huán)境等因素,理論上不存在誤報(bào).因此本系統(tǒng)采用基于動(dòng)態(tài)分析的方法.
動(dòng)態(tài)分析所采用的代碼插樁技術(shù)通常對(duì)程序的中間表示[11]或二進(jìn)制代碼插樁[12],導(dǎo)致無法報(bào)告錯(cuò)誤在源代碼中的準(zhǔn)確位置,給錯(cuò)誤調(diào)試帶來障礙.因此本系統(tǒng)采用源代碼插樁技術(shù)[13],即直接對(duì)源代碼進(jìn)行重寫,在檢測到錯(cuò)誤時(shí),可以報(bào)告錯(cuò)誤所在的源文件名和行號(hào)等.
圖1展示了Movec 系統(tǒng)的總體結(jié)構(gòu),主要由C 程序解析器和遞歸式AST 訪問者組成.對(duì)于用戶給定的C 程序,Movec 首先使用C 程序解析器將程序源代碼解析為AST,然后通過遞歸式遍歷和訪問AST 上的每個(gè)節(jié)點(diǎn),分析程序語義,并插入相應(yīng)的用于錯(cuò)誤檢測的代碼片段,生成插樁后的源代碼和接口文件(包括實(shí)現(xiàn)錯(cuò)誤檢測算法的數(shù)據(jù)結(jié)構(gòu)和接口函數(shù)).用戶通過編譯插樁后的源代碼,得到插樁后的可執(zhí)行程序.最后用戶運(yùn)行該程序,實(shí)現(xiàn)對(duì)內(nèi)存錯(cuò)誤的動(dòng)態(tài)檢測.
內(nèi)存錯(cuò)誤動(dòng)態(tài)檢測的基本思想是為每個(gè)指針變量分配一個(gè)指針元數(shù)據(jù)(Pointer Meta Data,PMD),用于記錄指針變量所指向的內(nèi)存塊的下界、上界和狀態(tài)信息,并在程序通過指針變量訪問內(nèi)存時(shí)檢測指針元數(shù)據(jù)所記錄的信息是否允許這次訪問.下面我們通過實(shí)例介紹錯(cuò)誤檢測算法的基本思想.
圖1 系統(tǒng)結(jié)構(gòu)圖
實(shí)例1.下列C 程序首先將指針p 初始化為指向變量i,然后對(duì)指針p+5 進(jìn)行解引用和賦值.顯然,由于p+5 超出了變量i 所在內(nèi)存塊的范圍,因此會(huì)發(fā)生內(nèi)存溢出錯(cuò)誤.
int i=1,*p=&i;
*(p+5)=1;
我們的檢測算法可以發(fā)現(xiàn)該錯(cuò)誤.在程序初始化指針p 之后,檢測算法首先為指針p 分配一個(gè)指針元數(shù)據(jù)(如圖2所示),用于記錄p 所指向的內(nèi)存塊的下界&i、上界&i+1 和狀態(tài)stack,其中stack 表示該內(nèi)存塊(變量i)是被存儲(chǔ)在棧中的有效變量(即沒有被釋放),然后將該指針元數(shù)據(jù)插入指針元數(shù)據(jù)表,并使用p 的地址&p 作為該元數(shù)據(jù)的索引值,用于加快查找指針元數(shù)據(jù)的速度.
圖2 實(shí)例1 中指針p 的指針元數(shù)據(jù)
在程序?qū)χ羔榩+5 進(jìn)行解引用訪問之前,檢測算法首先使用p 的地址&p 作為索引值在指針元數(shù)據(jù)表中查找p 的指針元數(shù)據(jù),然后檢測程序即將訪問的內(nèi)存塊范圍是否在指針元數(shù)據(jù)所記錄的下界和上界之間.由于程序即將訪問的內(nèi)存塊范圍是從&i+5 到&i+6,超出了元數(shù)據(jù)所記錄的從&i 到&i+1 的范圍,因此存在內(nèi)存溢出錯(cuò)誤.此時(shí)檢測算法將報(bào)告內(nèi)存錯(cuò)誤及其所在的源文件名、行號(hào)、列號(hào)和導(dǎo)致錯(cuò)誤的內(nèi)存訪問表達(dá)式“*(p+5)”.
實(shí)例2.下列C 程序首先在函數(shù)foo 中將指針n 初始化為指向函數(shù)foo 中的局部變量x,然后在退出函數(shù)foo 后對(duì)指針n 進(jìn)行解引用和賦值.顯然,由于n 所指向的變量x 此時(shí)是無效變量(即已經(jīng)被釋放),因此會(huì)發(fā)生內(nèi)存釋放后訪問的錯(cuò)誤.
我們的檢測算法可以發(fā)現(xiàn)該錯(cuò)誤.在程序初始化指針n 之后,檢測算法首先為指針n 分配一個(gè)指針元數(shù)據(jù)(如圖3(a)所示),用于記錄n 所指向的內(nèi)存塊的下界&x、上界&x+1 和狀態(tài)stack,然后將該指針元數(shù)據(jù)插入指針元數(shù)據(jù)表,并使用n 的地址&n 作為該元數(shù)據(jù)的索引值.
圖3 實(shí)例2 中指針n 的指針元數(shù)據(jù)及其變化
在程序退出函數(shù)foo 之前,程序釋放函數(shù)foo 中所有的局部變量,檢測算法將n 的指針元數(shù)據(jù)中的狀態(tài)改為invalid,表示n 所指向的內(nèi)存塊(變量x)已經(jīng)被釋放(如圖3(b)所示).
在程序?qū)χ羔榥 進(jìn)行解引用訪問之前,檢測算法首先使用n 的地址&n 作為索引值在指針元數(shù)據(jù)表中查找n 的指針元數(shù)據(jù),然后檢測指針元數(shù)據(jù)所記錄的狀態(tài)是否不是invalid.由于此時(shí)元數(shù)據(jù)所記錄的狀態(tài)是invalid,因此存在內(nèi)存釋放后訪問的錯(cuò)誤.此時(shí)檢測算法將報(bào)告內(nèi)存錯(cuò)誤及其所在的源文件名、行號(hào)、列號(hào)和導(dǎo)致錯(cuò)誤的內(nèi)存訪問表達(dá)式“*n”.
我們通過以下3 個(gè)接口函數(shù)來實(shí)現(xiàn)以上錯(cuò)誤檢測算法的主要功能:
1)接口函數(shù)pmd_tbl_update_as 用于更新給定指針變量p 的指針元數(shù)據(jù),將&p 所索引的指針元數(shù)據(jù)的內(nèi)容設(shè)置為給定的下界、上界和狀態(tài)信息.
2)接口函數(shù)pmd_tbl_lookup 用于獲取給定指針變量p 的指針元數(shù)據(jù),返回&p 所索引的指針元數(shù)據(jù)的地址.
3)接口函數(shù)check_dpv 用于檢測要訪問的內(nèi)存塊范圍是否在指針元數(shù)據(jù)所記錄的下界和上界之間、狀態(tài)是否有效.
接下來我們需要實(shí)現(xiàn)對(duì)程序源代碼的自動(dòng)插樁,使得程序能在運(yùn)行時(shí)調(diào)用以上接口實(shí)現(xiàn)對(duì)內(nèi)存錯(cuò)誤的動(dòng)態(tài)檢測.算法1 給出了自動(dòng)插樁的基本思想:通過遞歸式遍歷和訪問程序源代碼AST 上的每個(gè)節(jié)點(diǎn),在合適的位置插入對(duì)以上接口的調(diào)用.例如,在指針變量定義之后插入對(duì)其pmd 的初始化操作,在指針變量賦值語句之后插入對(duì)其pmd 的更新操作,在指針解引用表達(dá)式之前插入對(duì)比其pmd 和實(shí)際訪問的內(nèi)存塊范圍的檢測操作.
算法1.內(nèi)存錯(cuò)誤動(dòng)態(tài)檢測的自動(dòng)插樁算法遍歷程序源代碼AST,對(duì)于每個(gè)訪問到的節(jié)點(diǎn):1)如果該節(jié)點(diǎn)是指針變量定義,例如“T *p;”或帶有空初始值“T*p=NULL;”,則在該語句后插入接口調(diào)用:pmd_tbl_update_as(&p,NULL,NULL,NULL);2)如果該節(jié)點(diǎn)是從指針常量到指針變量的賦值語句,例如“p=&i;”,則在該語句后插入接口調(diào)用:pmd_tbl_update_as(&p,&i,&i+1,i_status);3)如果該節(jié)點(diǎn)是從指針變量到指針變量的賦值語句,例如“p1=p;”,“p1=p+I;”或“p1=&p[i];”,則在該語句后插入接口調(diào)用:pmd=pmd_tbl_lookup(&p);pmd_tbl_update_as(&p1,pmd->base,pmd->bound,pmd->status);4)如果該節(jié)點(diǎn)包含指針解引用表達(dá)式,例如“i=*p1”或“*p1=i”,則在該語句前插入接口調(diào)用:check_dpv(pmd_tbl_lookup(&p1),p1,sizeof(*p1));然后遞歸訪問該節(jié)點(diǎn)的子節(jié)點(diǎn).
Movec 系統(tǒng)的開發(fā)包括兩部分:使用C 程序設(shè)計(jì)語言實(shí)現(xiàn)錯(cuò)誤檢測算法,使用C++程序設(shè)計(jì)語言實(shí)現(xiàn)源代碼自動(dòng)插樁算法.為了用戶可以在不同的平臺(tái)上使用Movec,我們充分利用C/C++的跨平臺(tái)特性,采用基于CMake 編譯系統(tǒng)的跨平臺(tái)架構(gòu),使得Movec 可以在Linux 和Windows 上運(yùn)行.
由于C 程序設(shè)計(jì)語言并不自帶容器類數(shù)據(jù)結(jié)構(gòu),因此我們從零開始實(shí)現(xiàn)了一個(gè)高效的指針元數(shù)據(jù)表.如圖4所示,指針變量ptr 的指針元數(shù)據(jù)包括該指針變量的地址&ptr、所指向內(nèi)存塊的下界base、上界bound 和狀態(tài)信息節(jié)點(diǎn)的地址&st.由于多個(gè)指針(例如ptr1 和ptr2)可能指向同一個(gè)內(nèi)存塊,因此內(nèi)存塊的狀態(tài)信息節(jié)點(diǎn)必須是一個(gè)獨(dú)立的節(jié)點(diǎn),這樣才可能被多個(gè)指針元數(shù)據(jù)引用.整個(gè)指針元數(shù)據(jù)表是使用指針變量地址為索引值的哈希表.圖5展示了指針元數(shù)據(jù)結(jié)構(gòu)定義的UML 圖,其中指針變量的地址是指向指針的指針類型,下界和上界是指針類型,狀態(tài)信息節(jié)點(diǎn)的地址是指向狀態(tài)信息節(jié)點(diǎn)的指針類型.在狀態(tài)信息節(jié)點(diǎn)中,我們使用無符號(hào)整數(shù)表示內(nèi)存塊的當(dāng)前狀態(tài),例如invalid、stack、heap 等.
圖4 指針元數(shù)據(jù)表
圖5 指針元數(shù)據(jù)
基于指針元數(shù)據(jù)表,實(shí)現(xiàn)錯(cuò)誤檢測算法的3 個(gè)接口函數(shù)是較為直接的.例如,接口函數(shù)pmd_tbl_update_as先在指針元數(shù)據(jù)表中查找給定指針變量p 的指針元數(shù)據(jù),如果未找到則插入一個(gè)新的指針元數(shù)據(jù),然后對(duì)該元數(shù)據(jù)中的成員變量進(jìn)行賦值.
自動(dòng)插樁實(shí)現(xiàn)的關(guān)鍵在于遞歸式AST 訪問者的實(shí)現(xiàn).為了展示面向?qū)ο蟪绦蛟O(shè)計(jì)的特性,在實(shí)現(xiàn)中融合了封裝、繼承和多態(tài).首先,設(shè)計(jì)了一個(gè)基本的AST訪問者類ASTVisitorBase,包含對(duì)程序源代碼AST 上各種節(jié)點(diǎn)進(jìn)行訪問的成員虛函數(shù)VisitX,然后實(shí)現(xiàn)了Traverse 成員函數(shù),在對(duì)AST 進(jìn)行遞歸式遍歷的同時(shí)調(diào)用VisitX 函數(shù)來對(duì)AST 上的每個(gè)節(jié)點(diǎn)進(jìn)行訪問,其中X 可以是任意類型的節(jié)點(diǎn),例如訪問聲明Decl 的函數(shù)VisitDecl 和訪問語句Stmt 的函數(shù)VisitStmt 等.然后,設(shè)計(jì)了一個(gè)實(shí)現(xiàn)插樁的AST 訪問者類ASTVisitor,繼承于ASTVisitorBase,并對(duì)其中的成員虛函數(shù)VisitX 進(jìn)行虛函數(shù)重載,實(shí)現(xiàn)源代碼插樁功能.
Movec 的使用非常簡便.用戶只需要執(zhí)行3 個(gè)命令即可得到檢測結(jié)果:運(yùn)行Movec 對(duì)源代碼進(jìn)行自動(dòng)插樁;運(yùn)行編譯器將插樁后的源代碼編譯為可執(zhí)行程序;運(yùn)行插樁后的可執(zhí)行程序得到檢測結(jié)果.例如,圖6和圖7分別展示了對(duì)第2 節(jié)中的實(shí)例1 和實(shí)例2 進(jìn)行檢測的過程.結(jié)果顯示,實(shí)例1 中對(duì)指針p+5 的解引用導(dǎo)致了空間錯(cuò)誤(即內(nèi)存溢出錯(cuò)誤),實(shí)例2 中對(duì)指針n 的解引用導(dǎo)致了時(shí)間錯(cuò)誤(即內(nèi)存釋放后訪問).值得注意的是,檢測結(jié)果也報(bào)告了錯(cuò)誤在源代碼中的精確位置,包括源文件名和行號(hào)等.
圖6 實(shí)例1 的檢測結(jié)果
圖7 實(shí)例2 的檢測結(jié)果
Movec 可以被用于C 程序設(shè)計(jì)語言的教學(xué),有效提高教學(xué)效果.首先,學(xué)生可以對(duì)自己編寫的程序運(yùn)行Movec,更快地找到程序中的內(nèi)存錯(cuò)誤,極大地縮短調(diào)試時(shí)間,提高學(xué)習(xí)效率.通過長期使用Movec,學(xué)生能養(yǎng)成正確運(yùn)用指針和內(nèi)存操作的習(xí)慣,提高編程能力.第二,Movec 的錯(cuò)誤檢測函數(shù)庫可以作為C 語言的演示系統(tǒng),幫助學(xué)生掌握C 語言中一些復(fù)雜的理論知識(shí).函數(shù)庫的實(shí)現(xiàn)不僅大量使用了基本的條件語句、循環(huán)語句、數(shù)組、函數(shù)等,還覆蓋了較為復(fù)雜的指針和內(nèi)存操作、結(jié)構(gòu)體和鏈表等知識(shí).例如,指針元數(shù)據(jù)采用結(jié)構(gòu)體實(shí)現(xiàn),并包含了指向狀態(tài)信息節(jié)點(diǎn)的指針;指針元數(shù)據(jù)表采用基于動(dòng)態(tài)內(nèi)存分配的哈希表實(shí)現(xiàn),并采用鏈表來處理哈希沖突.這可以幫助學(xué)生深入理解這些理論知識(shí),并掌握如何將這些知識(shí)正確地運(yùn)用到編程實(shí)踐中.
Movec 可以被用于面向?qū)ο蟪绦蛟O(shè)計(jì)語言(C++)的教學(xué),有效提高教學(xué)效果.Movec 的源代碼自動(dòng)插樁模塊可以作為C++語言的演示系統(tǒng),幫助學(xué)生掌握C++語言中一些抽象的理論知識(shí).插樁模塊的實(shí)現(xiàn)覆蓋了重要但抽象的封裝、繼承和多態(tài)等知識(shí).例如,AST中所有的語法節(jié)點(diǎn)類型都采用了封裝的類來實(shí)現(xiàn),并且采用繼承來描述相關(guān)節(jié)點(diǎn)類型之間的聯(lián)系;遞歸式AST 訪問者的實(shí)現(xiàn)也采用了封裝、繼承和多態(tài),即繼承于一個(gè)基本的AST 訪問者類,并對(duì)其中的AST 節(jié)點(diǎn)訪問函數(shù)進(jìn)行虛函數(shù)重載;通過這些設(shè)計(jì)改善了大規(guī)模代碼的模塊化、可維護(hù)性、可復(fù)用性和健壯性.這可以幫助學(xué)生深入理解這些面向?qū)ο筇匦缘膬?yōu)點(diǎn),并主動(dòng)地將這些知識(shí)合理地運(yùn)用到編程實(shí)踐中.
Movec 也可以作為數(shù)據(jù)結(jié)構(gòu)、編譯原理、軟件工程、軟件測試等課程的案例分析材料,全面貫穿計(jì)算機(jī)、軟件工程等專業(yè)的培養(yǎng)計(jì)劃.
程序設(shè)計(jì)是計(jì)算機(jī)、軟件工程等工科專業(yè)的第一門核心必修專業(yè)課程.熟練地掌握程序設(shè)計(jì)技能不僅是學(xué)習(xí)后續(xù)專業(yè)課程的基礎(chǔ),也是獲得理想工作機(jī)會(huì)的重要條件.筆者在教學(xué)實(shí)踐中發(fā)現(xiàn),已有的教學(xué)系統(tǒng)只支持網(wǎng)絡(luò)教學(xué)組織[3,4]和在線判題[5–7],并不能幫助學(xué)生掌握課程難點(diǎn).為此,我們?cè)O(shè)計(jì)和實(shí)現(xiàn)了面向程序設(shè)計(jì)課程的教學(xué)系統(tǒng):C 程序內(nèi)存安全性動(dòng)態(tài)分析系統(tǒng).該系統(tǒng)綜合運(yùn)用了C 程序設(shè)計(jì)、面向?qū)ο蟪绦蛟O(shè)計(jì)(C++)、數(shù)據(jù)結(jié)構(gòu)、編譯原理、軟件工程、軟件測試等計(jì)算機(jī)和軟件工程專業(yè)核心課程的知識(shí),同時(shí)可以展現(xiàn)這些知識(shí)之間的深度融合.從教學(xué)效果來看,該系統(tǒng)不僅可以幫助學(xué)生理解和掌握相關(guān)的理論知識(shí),以及這些理論知識(shí)在實(shí)際軟件開發(fā)過程中的應(yīng)用,還可以激發(fā)學(xué)生學(xué)習(xí)的主動(dòng)性和鉆研的興趣,從而有效地提高教學(xué)效果.