陳圣磊, 王紅霞, 劉林源
(南京審計(jì)大學(xué) 電子商務(wù)系,江蘇 南京 211815)
C語(yǔ)言程序設(shè)計(jì)中文件操作部分的教學(xué)方法探討
陳圣磊, 王紅霞, 劉林源
(南京審計(jì)大學(xué) 電子商務(wù)系,江蘇 南京 211815)
學(xué)習(xí)C語(yǔ)言,不僅要求學(xué)生能夠掌握基本語(yǔ)法知識(shí),學(xué)會(huì)寫程序,還應(yīng)該深入理解庫(kù)函數(shù)提供的功能是如何實(shí)現(xiàn)的,以達(dá)到與學(xué)過(guò)的語(yǔ)法知識(shí)融會(huì)貫通的目的。文章通過(guò)闡述在講解文件操作時(shí)如何呼應(yīng)已學(xué)過(guò)的知識(shí)點(diǎn),包括結(jié)構(gòu)體、函數(shù)和指針,從而加深對(duì)文件操作以及相應(yīng)語(yǔ)法知識(shí)點(diǎn)的理解。
程序設(shè)計(jì);庫(kù)函數(shù);C語(yǔ)言;文件操作
C語(yǔ)言程序設(shè)計(jì)是計(jì)算機(jī)相關(guān)專業(yè)的一門學(xué)科基礎(chǔ)課,承擔(dān)著培養(yǎng)學(xué)生編程思想的重要任務(wù)。這門課程涵蓋了C語(yǔ)言的所有語(yǔ)法,包括數(shù)據(jù)類型與變量、流程控制結(jié)構(gòu)、數(shù)組、函數(shù)、指針和結(jié)構(gòu)體等。學(xué)習(xí)這些基本語(yǔ)法的目的有兩方面:一是學(xué)會(huì)寫程序;二是幫助學(xué)生讀懂庫(kù)函數(shù),深入理解庫(kù)函數(shù)提供的功能是如何實(shí)現(xiàn)的。第一個(gè)目的很容易理解,之所以提出第二個(gè)目的,是因?yàn)閹?kù)函數(shù)的定義與調(diào)用涉及很多基本語(yǔ)法知識(shí),在學(xué)習(xí)庫(kù)函數(shù)時(shí)如果能與這些語(yǔ)法點(diǎn)呼應(yīng)起來(lái),就能達(dá)到融會(huì)貫通的目的。
然而,目前的計(jì)算機(jī)教育對(duì)前一方面關(guān)注較多[13],而忽視了第二個(gè)方面。造成的結(jié)果是學(xué)生理解了流程控制、數(shù)組、函數(shù)和指針的用法,也能夠編寫簡(jiǎn)單的程序,但是對(duì)程序的認(rèn)識(shí)僅限于教材上的例題,對(duì)庫(kù)函數(shù)提供的功能,如字符串處理、輸入輸出處理,理解不夠深入,未能把學(xué)過(guò)的語(yǔ)法知識(shí)學(xué)以致用,去理解這些函數(shù)。
文件操作是在講解完基本語(yǔ)法后遇到的一個(gè)相對(duì)獨(dú)立的知識(shí)模塊,可以讓學(xué)生學(xué)會(huì)使用相關(guān)函數(shù)來(lái)處理文件讀寫的問(wèn)題。那么在文件操作部分的教學(xué)中,不但要讓學(xué)生掌握如何使用這些函數(shù),還應(yīng)該把之前學(xué)習(xí)的語(yǔ)法貫穿到這些知識(shí)點(diǎn)中。
我們一般使用文件類型FILE的方法是聲明FILE類型的指針變量,
FILE* fp;
當(dāng)同學(xué)們看到這句聲明時(shí),自然會(huì)想到FILE應(yīng)該是一個(gè)類型,那么這個(gè)類型究竟是怎么構(gòu)成的呢?
其實(shí)教材對(duì)文件類型FILE講得比較清楚[4]333:每個(gè)被使用的文件都在內(nèi)存中開(kāi)辟一個(gè)相應(yīng)的文件信息區(qū),用來(lái)存放文件的有關(guān)信息(如文件的名字、文件狀態(tài)及文件當(dāng)前位置等)。這些信息被保存在一個(gè)結(jié)構(gòu)體變量中,該結(jié)構(gòu)體的類型是由系統(tǒng)聲明的,取名為FILE。它的類型聲明為:
從文件類型FILE的定義中,我們可以看到:
(1)文件類型FILE是一個(gè)結(jié)構(gòu)體類型,由表示緩沖區(qū)大小、位置、讀寫控制等的字段組成,這可以呼應(yīng)到定義結(jié)構(gòu)體類型的語(yǔ)法。
(2)FILE是用typedef定義的新類型,因此在使用FILE定義指針時(shí)不再需要struct關(guān)鍵字,這對(duì)應(yīng)到typedef關(guān)鍵字的用法。
(3)上述定義是教材中給出的,我們還可以在VC++6.0中打開(kāi)stdio.h文件,找到對(duì)FILE的定義:
typedef struct _iobuf FILE;
我們可以看到,在VC++6.0中,先定義了結(jié)構(gòu)體類型_iobuf,再由typedef關(guān)鍵字定義類型FILE。其中,字段_base表示緩沖區(qū)的開(kāi)始位置,_ptr表示指針的指向,_bufsiz表示緩沖區(qū)的大小。
因此,通過(guò)上述解釋,學(xué)生更容易理解FILE其實(shí)就是結(jié)構(gòu)體類型,這樣不僅加深了對(duì)FILE類型的理解,也通過(guò)這樣一個(gè)例子加深了對(duì)結(jié)構(gòu)體語(yǔ)法的理解。
我們一般使用以下程序打開(kāi)文件:
其功能是按照只讀的方式打開(kāi)文件,指針變量fp指向被打開(kāi)的文件。除此之外,我們還可以對(duì)fopen函數(shù)作以下深層次的挖掘:
(1)使用這個(gè)函數(shù)需要包含頭文件stdio.h。那么在講函數(shù)原型部分時(shí)說(shuō)過(guò),如果使用庫(kù)函數(shù),應(yīng)該使用#include指令包含相關(guān)的頭文件,如果使用用戶自定義的函數(shù),應(yīng)該添加對(duì)被調(diào)用函數(shù)的聲明。那么我們可以進(jìn)一步提出問(wèn)題:這兩種方式之間有關(guān)聯(lián)嗎?頭文件中究竟是什么內(nèi)容呢?
這里,我們可以在VC++6.0中打開(kāi)頭文件stdio.h,查找fopen,結(jié)果發(fā)現(xiàn):
_CRTIMP FILE * __cdecl fopen(const char *, const char *);
其中,_CRTIMP是一個(gè)宏,表示使用動(dòng)態(tài)C運(yùn)行時(shí)庫(kù)還是靜態(tài)連接的C運(yùn)行庫(kù),__cdecl表示C語(yǔ)言默認(rèn)的函數(shù)調(diào)用方法,可以忽略。其余內(nèi)容就是函數(shù)的聲明了,返回值為FILE指針,兩個(gè)參數(shù)都是字符指針類型。
通過(guò)查看頭文件,我們知道頭文件中其實(shí)就是函數(shù)的聲明。所以,所有的函數(shù)調(diào)用都需要函數(shù)聲明,只不過(guò)庫(kù)函數(shù)的聲明集中在頭文件中,因此只要用#include包含頭文件就可以了。
(2)fopen函數(shù)的返回值為FILE指針類型。在函數(shù)部分講過(guò)返回指針值的函數(shù),但是比較簡(jiǎn)略。返回指針值的函數(shù)涉及以下3個(gè)問(wèn)題:
① 如果函數(shù)返回指針值,那么不能返回這個(gè)函數(shù)中局部變量的地址,因?yàn)榫植孔兞吭诤瘮?shù)調(diào)用結(jié)束時(shí)就釋放了,再引用這個(gè)地址就會(huì)帶來(lái)問(wèn)題。比如:
指針p指向funA函數(shù)中局部變量a,a的值為3,但是輸出*p的結(jié)果為4。這是由于在調(diào)用函數(shù)funA后,又調(diào)用函數(shù)funB,變量a的位置被重新分配變量b,b的值為4,所以此時(shí)輸出*p的結(jié)果為4。如果在主函數(shù)沒(méi)有調(diào)用funB,即刪除funB();這一行,則輸出*p的結(jié)果為3。由此可以看出,這種返回局部變量地址的做法存在很大的不確定性。其實(shí),上述程序在編譯時(shí)會(huì)產(chǎn)生警告:returning address of local variable or temporary(返回局部變量的地址)。因此,我們?cè)诰帉懗绦驎r(shí)應(yīng)當(dāng)避免這種情況。
② 被調(diào)用函數(shù)可以返回主調(diào)函數(shù)中變量的地址。教材[4]274例題8.25展示了這種情況,代碼如下:
從上述代碼可以看出,search函數(shù)的參數(shù)pointer指向主函數(shù)中的二維數(shù)組score,返回值pt是基于pointer得到的一個(gè)地址,仍然指向二維數(shù)組score中的某個(gè)元素。因此這種情況通過(guò)參數(shù)傳遞數(shù)組名,返回的是主函數(shù)中數(shù)組里某個(gè)元素的地址,因此在子函數(shù)調(diào)用結(jié)束后不存在地址釋放的問(wèn)題。要注意這種情況和①中程序是有區(qū)別的。
③ 函數(shù)fopen并沒(méi)有指針類型的參數(shù),那么為什么它返回指針類型的返回值就沒(méi)有問(wèn)題呢?其實(shí)我們可以想象,fopen函數(shù)中應(yīng)該聲明一個(gè)FILE類型的變量,最后返回其地址。如果是普通局部變量,fopen函數(shù)返回時(shí)肯定會(huì)釋放的。那么什么樣的變量在函數(shù)結(jié)束時(shí)仍然不會(huì)釋放呢?我們?cè)谇懊嬷v過(guò),棧空間變量會(huì)自動(dòng)釋放,而堆空間變量是由程序員申請(qǐng)和釋放的,因此,這個(gè)變量就應(yīng)該是堆空間中動(dòng)態(tài)分配的變量,由此呼應(yīng)到動(dòng)態(tài)內(nèi)存分配的相關(guān)函數(shù)。
可以想象,fopen函數(shù)的定義中應(yīng)該包含以下代碼:
即該函數(shù)調(diào)用了malloc函數(shù),動(dòng)態(tài)分配了sizeof(FILE)大小的空間,賦值給fp指針,最后返回fp的值。
更進(jìn)一步,所有動(dòng)態(tài)分配的空間都需要通過(guò)free函數(shù)釋放,那么在哪里調(diào)用free函數(shù)呢?我們強(qiáng)調(diào)在使用fopen函數(shù)打開(kāi)文件進(jìn)行讀寫后,還應(yīng)該調(diào)用fclose函數(shù)關(guān)閉文件。因此可以想見(jiàn),在fclose函數(shù)的定義中應(yīng)該包含以下代碼:
因此,通過(guò)上述分析,學(xué)生不但掌握了打開(kāi)關(guān)閉文件的方法,更深入理解了函數(shù)聲明、返回指針值的函數(shù)與動(dòng)態(tài)內(nèi)存分配的相關(guān)問(wèn)題。
C語(yǔ)言允許通過(guò)函數(shù)fgets和fputs一次讀寫一個(gè)字符串,其中fgets函數(shù)的原型為[4]341:
char* fgets(char *str, int n, FILE *fp);
作用是從fp所指向的文件讀入一個(gè)長(zhǎng)度為n-1的字符串,并在最后加一個(gè)‘ 蒙阴县| 安仁县| 乐至县| 仲巴县| 武乡县| 布尔津县| 沂源县| 孟津县| 青浦区| 资溪县| 潍坊市| 陈巴尔虎旗| 芮城县| 措美县| 保定市| 沧源| 井陉县| 天津市| 普兰县| 宁陵县| 将乐县| 石台县| 汝阳县| 商都县| 枣阳市| 巨鹿县| 崇文区| 隆子县| 全南县| 义马市| 富裕县| 正阳县| 西吉县| 沁源县| 讷河市| 德州市| 开江县| 来凤县| 马尔康县| 延川县| 苍梧县|