周二強
(河南城建學(xué)院計算機與數(shù)據(jù)科學(xué)學(xué)院,平頂山467036)
數(shù)組是C語言教學(xué)中的重點和難點,本文提出虛擬變量的概念,直觀而深刻地揭示數(shù)組變量的本質(zhì)。把數(shù)組統(tǒng)一為變量,為學(xué)生自主理解辨析數(shù)組和指針變量鋪平了道路,也為反轉(zhuǎn)教學(xué)提供必要的條件,極大地提高學(xué)生的學(xué)習(xí)積極性。
虛擬變量;數(shù)組;指針;C語言
圖1 相關(guān)變量的存儲狀態(tài)簡圖
C語言是經(jīng)典的結(jié)構(gòu)化程序設(shè)計語言,其語法被現(xiàn)代編程語言廣泛借鑒,國內(nèi)許多高校都開設(shè)了C語言課程。國內(nèi)現(xiàn)行C語言教材大多強調(diào)實訓(xùn),注重編程能力的培養(yǎng),忽視了C語言和計算機的關(guān)系,導(dǎo)致C語言教材中的一些概念不清,既不利于學(xué)生系統(tǒng)分析能力的培養(yǎng),也不便于反轉(zhuǎn)課堂等教學(xué)改革的進行。近年來新出現(xiàn)的“反轉(zhuǎn)”課堂(Flipped Class,也譯為“翻轉(zhuǎn)”課堂)教學(xué)模式,引起了教育界的廣泛關(guān)注?!胺崔D(zhuǎn)”即師生互換角色,教師是主導(dǎo),學(xué)生是主體,這充分發(fā)揮了以人為本的教學(xué)思想和教師的主導(dǎo)作用[1]。在課堂教學(xué)活動中應(yīng)強調(diào)學(xué)生的參與意識,充分體現(xiàn)學(xué)生在課堂中的主體地位[2]。
數(shù)組是C語言教學(xué)中的重點。使用數(shù)組可以方便地重復(fù)處理大量的數(shù)據(jù),作為數(shù)據(jù)結(jié)構(gòu),數(shù)組的基本用法不難掌握。但由于現(xiàn)行教材對數(shù)組概念的模糊處理,使得多數(shù)學(xué)生不能自主學(xué)習(xí)有關(guān)知識點,更不要說積極主動地參與反轉(zhuǎn)課堂了,導(dǎo)致學(xué)習(xí)效果不理想,不會利用指針變量操作復(fù)雜的數(shù)組。
在C語言中,借助變量使用計算機中的存儲單元,一個變量標(biāo)識了計算機中的一塊存儲單元。存儲單元是內(nèi)存塊在C語言中的抽象,具有固定的大小,分類型,某類存儲單元只能存放該類數(shù)據(jù)。
語句int i;定義了一個整型(int)變量i,它對應(yīng)于一個整型(int)存儲單元,只能存儲一個整數(shù)(int)。語句int*pi;定義一個整型指針變量pi,它對應(yīng)于一個整型指針(int*)存儲單元,只能存儲一個整型存儲單元的地址(int*)。與普通變量存儲常見的數(shù)據(jù)不同,指針變量用于存儲地址。地址是存儲單元在計算機中的編號,用于定位存儲單元。
語句i=5;可以把整數(shù)5存入與變量i相關(guān)的存儲單元中。語句pi=&i;可以把與變量i相關(guān)的整型存儲單元的地址存入與變量pi相關(guān)的存儲單元中,此時常形象地稱指針變量pi指向了變量i。它們在內(nèi)存中的狀態(tài)可用圖1簡單地表示。
可簡單地認(rèn)為,變量i的值是5,指針變量pi的值是&i。由于地址屬于計算機的內(nèi)部數(shù)據(jù),而非用戶數(shù)據(jù),故對初學(xué)者而言,指針變量難以理解。指針變量的用法分兩步,首先,以直接引用的方式使用指針變量本身,如pi=&i;,使得指針變量指向變量i;然后,以間接引用的方式借助指針變量使用其指向的變量,如*pi=6;。盡管直接引用的方式簡便,但受變量作用域的限制。只要獲得了存儲單元的地址就能以間接引用的方式使用存儲單元,間接引用的方式可以擴展存儲單元的使用范圍[3]。
語句int a[3]={1,2,3};定義一個數(shù)組變量a,即申請了一塊存儲單元用于存儲整數(shù)1,2,3。數(shù)組變量a由3個整型變量組成,其內(nèi)存狀態(tài)可能如圖2所示。
圖2 數(shù)組a的內(nèi)存狀態(tài)
由圖2可知,數(shù)組變量a沒有專屬于自己的存儲單元,并不是一個真正的變量,數(shù)組變量a的值是C語言規(guī)定的。從這個角度分析,數(shù)組變量a是一個符號常量。但sizeof(a)的值是12,即數(shù)組變量a的存儲單元有12個字節(jié),數(shù)組元素的存儲單元都屬于數(shù)組變量a,所以,不能簡單地把數(shù)組變量a理解為普通的符號常量。數(shù)組a應(yīng)理解成一個虛擬的變量[3]。數(shù)組變量a指向了其首元素,數(shù)組變量a與其首元素a[0]的關(guān)系可用圖3表示。
圖3 虛擬的數(shù)組變量a
數(shù)組變量a是一個int[3]型存儲單元。由圖3可知,其地址和值均為0x0012ff00,但這兩個地址的意義不同,其地址(&a)是一個int[3]型地址,其值(a)是一個int地址(&a[0]),即&a+1指向下一個int[3]型存儲單元,其值為0x0012ff12,但a+1指向a[1],其值為0x0012ff04。
以二維數(shù)組int a[3][2]={{1,2},{21,22},{31,32}}為例,它的內(nèi)存狀態(tài)可能如圖4(1)所示。
圖4 二維數(shù)組a的內(nèi)存狀態(tài)
二維數(shù)組a有3個數(shù)組元素a[0]、a[1]和a[2],數(shù)組元素的類型是長度為2的一維整型數(shù)組,即它們標(biāo)識一個int[2]型存儲單元。
二維數(shù)組變量a是一個虛擬的變量,它標(biāo)識的存儲單元的長度為24個字節(jié)(sizeof(a)的值為24),類型為int[3][2]。二維數(shù)組變量a也指向了首元素a[0],其值為int[2]型地址0x0012 ff00,*a與a[0]可以互換使用,sizeof(*a)與sizeof(a[0])的值為8。
一維數(shù)組a[0]所標(biāo)識的存儲單元長度為8個字節(jié),類型為int[2]。一維數(shù)組變量a[0]指向了首元素a[0][0],其值為int型地址0x0012 ff00,*a[0]與a[0][0]可互換使用,sizeof(*a[0])或sizeof(a[0][0])的值為4。
二維數(shù)組變量a,一維數(shù)組變量a[0],整型變量a[0][0]三者的關(guān)系如圖4(2)所示。
如有int*p,則指針變量p不能用二維數(shù)組a賦值,因為二維數(shù)組變量a的首元素a[0]為int[2]型,即長度為2的一維整型數(shù)組,而指針變量p只能存儲int型地址。可以存儲int[2]型地址的指針變量理想的定義方式為int[2]*p,而C語言中實際的定義方式為int(*p)[2]。定義了可以指向int[2]型存儲單元的指針變量p后,就可以用p=a(或p=&a[0])讓指針變量p指向數(shù)組a的首元素a[0]了。此時指針變量p、二維數(shù)組變量a、一維數(shù)組變量a[0]及a[0][0]的關(guān)系如圖5所示。
圖5 二維數(shù)組與指向其首元素的指針變量
下面程序用三種方式輸出了二維數(shù)組a的數(shù)組元素。
分析:
p=a;執(zhí)行后,指針變量p也指向了數(shù)組a的首元素a[0],故p+i指向了a[i],*(p+i)與a[i]標(biāo)識了同一個長度為2的一維整型數(shù)組,表達式(*(p+i))[j]標(biāo)識了該數(shù)組型存儲單元的第j個數(shù)組元素,表達式(*(p+i))[j]與a[i][j]可互換。
pi=a[i];執(zhí)行后,pi也指向了a[i]的首元素a[i][0],故pi+j指向了a[i][j],因此表達式*(pi+j)與a[i][j]標(biāo)識同一個整型存儲單元。
pi=a[0];執(zhí)行后,pi也指向了a[0]的首元素a[0][0]。表達式*pi++求值時,兩個操作符優(yōu)先級相同,右結(jié)合,先計算子表達式pi++,值為pi,原表達式的值為*pi,但求值的同時,變量pi的值自增1,pi指向了與原指向的存儲單元相鄰的下一個同類型存儲單元。第一次求值時,表達式*pi++與a[0][0]標(biāo)識了同一個存儲單元,且pi指向了a[0][1];第二次求值時,表達式*pi++與a[0][1]標(biāo)識了同一個存儲單元,且pi指向了a[0][2];……。由于數(shù)組的數(shù)組元素相鄰,故通過重復(fù)地輸出表達式*pi++的值也可以輸出數(shù)組a的所有數(shù)組元素。
指針的使用,極大地提高了C語言的執(zhí)行效率,但地址的存在,增加了C語言的學(xué)習(xí)難度。給初學(xué)者提供一個直觀且準(zhǔn)確的概念,不僅可以極大地降低學(xué)習(xí)難度,不會造成C語言知識點的殘缺,而且也為啟發(fā)式教學(xué)提供了可能,教學(xué)效果顯著。