吳家洲, 張 勝, 劉 君
(南昌航空大學信息工程學院,南昌 330063)
工業軟件的發展被列為當前科技攻關最緊急和最迫切的問題之一[1]。C 語言作為基礎性的開發軟件,同時具備底層和高級語言開發的特性,重要性無可替代。Microsoft Windows、Linux、Apple MacOS 和Google Android等代表性操作系統的大部分代碼由C 語言編寫,辦公軟件、數據庫和郵件系統等用戶級別開發也離不開C 語言。國內外大學大量工科類專業把C 語言作為學生學習程序設計的入門語言,并且對課程教學改革做了很多有益的嘗試。計算思維是把待求問題用特定符號描述出來,同時依據模塊之間關系建立模型,利用計算機的思維方式對模型自動求解的過程[2]。文獻[3]中采用計算思維方式詳細描述了C 語言程序中的抽象模塊,在EIP 寄存器的控制下實現程序的自動執行。計算思維在課程的理論、實踐和線上MOOC教學過程中,都取得了較好的效果[4-6]。隨著信息技術的發展,以線上視頻和線上測試為基礎的翻轉課堂在教學中發揮重要作用[7-8]。此外,以學科競賽和項目驅動為引導的C 語言教學被廣泛采用[9-11]。近年來,結合計算機硬件的軟件類課程教學越來越受到重視,并且出版了相關教材[12-15]。然而,C語言教學一直存在兩個主要問題:①基于語言層面的教學,沒有結合支撐程序運行的硬件和操作系統相關理論,側重于數據結構和算法,對程序運行本質缺乏基本認識,不能深入完整理解代碼的執行過程;②對于大一新生,C語言與高中及以前所學知識缺乏延續性,上大學之前絕大部分學生沒有經過這方面訓練和培訓,不具備基本的編程思維。
依據C語言與計算機原理課程的聯系,通過微課形式補充計算機基本理論知識,加深學生對C 語言程序執行過程的全面理解。本文采用線上線下的混合教學方法,并增開了簡單的嵌入式系統實驗,使學生明確程序設計的具體應用。
與C語言相關的計算機基本理論課程主要包括:嵌入式系統設計、計算機組成原理和計算機控制系統等。計算機組成原理是學習計算機理論的入門課程,介紹了計算機系統的整體結構和各組成模塊內容[16];嵌入式系統就是一個微型計算機系統,有自己的硬件結構和操作系統[17-18];計算機控制系統主要講述的是計算機在控制領域的具體應用。選取計算機組成原理和嵌入式系統(ARM Cortex-M3 內核)兩門課中與C語言程序設計相關的內容,制作成微課視頻,供學生課外學習,其中的重點內容也會在理論課上詳細講解。教學內容及對應關系如表1 所示,其中,指令的執行過程、存儲器映射、尋址方式和棧是C 語言程序設計中重要的理論支撐,是培養學生程序設計能力和編程思維的核心內容。

表1 C語言教學內容及與計算機原理的對應關系
實踐性環節,引入了STM32F103 微控制器的基本實驗:LED流水燈控制,使學生對C 語言實際應用有初步認識。學生不僅能用C 語言編程進行算法設計,也能進行硬件接口的驅動設計。
C語言課程知識點繁多且零散,教學內容前后交叉,再加上補充的計算機原理類知識,顯然僅僅通過課堂教學很難完成教學任務。課程采用線上線下混合式的教學方法:課前學生在超星網絡教學空間觀看微課視頻,自學C 語言基本語法和簡單的計算機基礎知識。理論課以多個小項目為驅動,引導學生綜合應用所學語法知識,采用計算思維模擬計算機執行過程來思考和設計程序,培養學生程序設計與分析能力。課后采用線上刷題方式鞏固所學知識點,鼓勵學生在藍橋杯設計大賽題庫及國內外高校C/C+ +題庫進行大量的訓練。根據布拉姆教育目標分類理論,聽課、看視頻和作業訓練屬于認知的最低層次,是學習知識的最基本要求。想要形成編程思想,必須對大量程序進行分析、歸納和評估。只有不斷地經過“調試-修改-再調試-再修改”循環,才能掌握程序設計要點,逐步形成編程思維。多年教學實踐也表明,要切實提高學生編程能力,大量的編程訓練必不可少。
C語言課程主要教學內容如表1 所示,包括:①程序設計的基本語法;②簡單的數據結構與算法設計。由于課程開課時間一般在大一上學期,而計算機組成原理和ARM嵌入式系統體系結構本身就比較復雜,因此采用微課方式補充少量基礎的計算機原理知識。使得學生不僅知道C 語言語法規定,還知道為什么要這樣規定,這樣規定的理論依據是什么。
計算機的中央處理器CPU 不能識別C 語言編寫的程序代碼,程序需要編譯鏈接轉變成二進制機器碼才能在硬件上執行。由圖1 可知,程序的運行過程為:①從存儲器讀取指令,將指令解碼并生成控制信號,同時,程序計數器PC 自動指向下一個地址;②依據CPU 控制器發出的指令從數據區讀取待處理的數據并暫存在CPU 的寄存器中,算術邏輯運算單元ALU按照控制器指令從CPU 寄存器取出數據完成相關的運算,并將計算結果返回給數據區。

圖1 程序的執行過程
從程序在硬件上運行過程可以看出:C 代碼執行時,將指令代碼和數據分別存放在內存的不同位置,CPU從內存中讀取數據在ALU中完成計算,并將計算結果返回給數據區;CPU的頻率大小影響程序執行的效率,程序執行也離不開操作系統的支持。C 程序代碼按照從上到下的順序執行,是由于程序計數器PC自動指向下一個地址的結果。C語言在語法層面的規定是硬件層實現的體現。
計算機處理的數據可分為數值型數據和非數值型數據,其發展初期主要是數值計算。計算機硬件由數字邏輯電路組成,只能識別高、低電平。內部機器碼由0,1 二進制組成,一般情況下,“0”代表低電平;“1”代表高電平。依據人們對數據認識的先后順序,介紹常用的正數和負數、整數和小數在計算機內部硬件層的表示,以及簡單的整數加減運算過程,逐步培養學生興趣,強化學生對編程語言的感性認知。
正數的二進制原碼、反碼和補碼是相同的,負數的二進制補碼等于反碼最低位加1。為了將減法運算轉變成加法運算,計算機內部數據都采用補碼來表示,最高位為符號位,符號位是“0”表示正數,“1”表示負數,每8 位1 byte。數據的表示有定點數和浮點數兩種方式,C 語言中的int 數據的用定點數格式表示,小數點在最后一位后面,實際上就是整數,其格式如圖2所示。C語言中float、double float 數據類型常用浮點數表示,普遍采用IEEE754 標準規定的格式。4 byte float數據類型格式如圖3 所示。表示的數據值大小為

圖2 定點數格式

圖3 IEEE754標準規定的浮點數格式
由圖2 和3 可見,同樣為32 位的int和float數據,在計算機內部的表示形式是不同的,對應數據值的計算方法也不同。因此,不同數據類型相互轉換必然存在精度的差異,先定義數據類型再進行數據運算是C語言的特點。
補碼的出現解決了計算機中數據減法的問題,計算機內部用加法運算來處理減法計算。在硬件層,通過邏輯與、邏輯非和邏輯或及其組合邏輯電路來實現定點數的運算。例如:十進制整數5 和6 的加法/減法的補碼計算過程如圖4 所示。浮點數計算與此不同,其計算過程非常復雜。

圖4 加/減法運算過程(補碼)
由此可見,C 語言中不同數據類型在計算機內部有不同的表示形式,具有不同的存儲方式。因此,數據在使用前必須先定義其數據類型,便于系統知道采用哪種格式存儲數據、調用數據和數據計算。不同數據之間也不能隨意轉換,避免造成精度的丟失。
函數是程序設計的核心內容,程序由各種具有特定功能的函數組成。包括主函數在內,函數調用都采用棧的數據結構來處理。棧是在內存上分配的一段存儲區間,只不過這段區間存儲的內容滿足“先進后出”的原則,即先壓入棧的變量后彈出使用。以主函數main()調用子函數為例,說明函數調用與棧的關系,如圖5 所示。

圖5 函數的調用與棧的關系
當子函數被調用時,系統會給子函數創建一個“棧幀”區域,棧底為高地址,向低地址擴展,并將函數地址壓入棧底,即epb(main)地址值存儲在棧底指針ebp中;接著,將子函數參數等數據值從高位到低位入棧,入棧數據均為局部變量,棧頂地址由棧頂指針esp存儲。調用完成后,esp 指針移動棧底指針ebp 位置,依據棧底指針ebp 存儲的返回地址程序返回到主函數,調用完成。
可以看出,系統給子函數分配“棧幀”的存儲區域,主函數與子函數之間的參數傳遞是單向的,不可能是雙向的。子函數內部的變量存儲在棧中,是局部變量,不能隨意改變。棧的機制決定了子函數參數不能改變主函數參數的值。函數調用完成后,子函數依據存儲在棧底的子函數地址返回主程序,同時,分配的棧空間被釋放。
指針本身就是硬件層地址在邏輯層的抽象。在計算機硬件結構中并不存在指針,只有地址。實際上,CPU是通過數據的地址讀取數據的,這從ARM Cortex-M3 存儲器映射完全能體現出來。
圖6 為ARM Cortex-M3 存儲器到TM2-TM7 定時器映射關系。圖的左邊是對4 GB(4 =232)存儲地址的分配,分為代碼區、SRAM區、外部設備、私有外設總線和供應商預留地址區。TM2 定時器起始地址0X40000000 正是左邊0.5 GB 外設區的起始地址,外設區的第1 個設備就是TM2 定時寄存器。通過地址0X40000000 就可以訪問TM2,實現對TM2 的操作。C語言中對數據的訪問同樣如此,也是通過地址找到數據,實現對數據的讀取和寫入。

圖6 存儲器到TM2-TM7定時器映射關系
圖7 為兩種尋址方式:直接尋址和間接尋址。依據指令地址直接找到數據的方式為直接尋址,而間接尋址是指由指令地址找到下一級地址,再由下一級地址找到數據的方式。

圖7 直接尋址與間接尋址
由此可見,C 語言中的指針只是一種抽象的邏輯概念,它不是具體存在于計算機內部。程序中指針變量存儲的是地址,計算機是通過地址實現對數據的訪問。
在課程“嵌入式系統設計”中,教學選擇的是以ARM Cortex-M3 處理器為核心的32 位微控制器STM32。可以在C語言實訓課程中,采用標準函數庫的編程方法,通過通用的輸入輸出GPIO 口實現LED流水燈控制的程序設計。


由圖8 可以看出,程序執行的邏輯順序由硬件電路的連接方式來確定,設置引腳為低電平0 時燈亮,為高電平1 時燈滅。系統函數頭文件stm32f10x. h 定義了應用層結構體文件和硬件層芯片引腳的映射關系,供開發者在程序中調用。stm32f10x _ gpio. h 和stm32f10x_rcc.h的用法與此類似。delay. h 是用戶自定義延時函數頭文件,是兩次燈亮之間的間隔時間。led.h是控制引腳置高、低電平時的自定義頭文件。

圖8 硬件連接圖
通過工程實例訓練可知,程序設計的目的是為了解決特定的工程問題。采用計算思維方式對工程問題進行抽象,用計算機能夠識別的數據類型來描述特定的量,設計一系列函數求解問題。雖然程序設計思維是抽象的,但解決的問題是實際的,多解決具體問題有利于編程思維的形成。程序設計不能簡單地記住一些語法規定,其最終目的是為了解決實際的工程問題。
工業軟件、人工智能和智能制造的發展已上升到國家戰略,高新技術產業發展需要大量具有程序設計與分析能力的高層次人才。C語言作為一門專業基礎課,編程思維的培養對于專業課程學習至關重要。充分考慮計算機組成及體系結構的難度,教學內容以微課的形式補充程序設計在計算機理論方面淺顯易懂的基礎知識。利用先進的信息技術,采用翻轉課堂和項目驅動的形式線上線下混合式教學。
教學實踐表明:多課程融合的教學改革促進了學生對語法規定的深入理解,培養了學生程序設計的認知能力和編程思維,提高了學生編程能力。同時,多課程的引入使得學生更加明確專業和個人的發展方向。課程考核在平均分、最高分和最低分上都有穩步的提高,畢業學生初步具備解決復雜工程問題的能力。