我的C語言學習曆程:起步篇

來源:互聯網
上載者:User
學習C語言的開始

你玩過電腦遊戲嗎?

你喜歡電腦遊戲嗎?

電腦遊戲是你生活中的一部分嗎?

小時候,遊戲是一台黑白電視機——現實在手柄的這頭,夢境在手柄的那頭.

有的遊戲改變過人的一生……

長大後,遊戲是一枚小小的銅幣——現實在搖杆的這頭,夢境在搖杆的那頭。

現在啊,遊戲是一台電腦——現實在滑鼠這頭,夢境在滑鼠那頭……

當電腦技術給遊戲提供了強有力的支援後,一個陌生而又似曾相識的新奇世界展示在人們面前。

這裡有逝去的童年夢想,有心頭壓抑已久的情感;有瘋狂、神秘、也有腦力和技巧的挑戰;有輕鬆獲得實實在在的知識,也有用“虛擬”成就一個別樣的人生。這不是一個神奇的世界嗎?下面,我們就用編遊戲來學習C語言吧!

總是從HELLO WORLD開始。

學習編程的第一個程式,一般就是列印一個親切的詞語——“Hello,World!”

……

(後面的內容省略)

 

 

透露一下,上面所寫的,其實是轉載自我的學習機裡的“趣味導學”開始部分的內容,我接觸C語言也是從這裡開始的。

高二時,我有了一部學習機,它搭載的系統是嵌入式linux系統,除了學習功能外,還內建了“編程天地”功能,看名字大家應該就能知道,這個是用來編程的。

 

開啟“編程天地”,主菜單裡面有個Noah-IDE,之前還不知道IDE是什麼,有空百度了一下,原來IDE指的是整合式開發環境;

這個Noah-IDE很強大,雖然沒有電腦端的那樣多的功能,比如:文法高亮、自動縮排、代碼格式化。

但是,準系統還是具備的:可以建立C/C++工程、為工程添加源碼檔案、支援編輯撤銷/重做、跳轉到指定行、編譯工程時自動產生makefile檔案並make之,更想不到的是,還內建了gcc、g++編譯器,busybox也有,標頭檔當然比較齊全。

閑話不多說,入正題。

之前看“趣味導學”裡的樣本源碼,第一個是HelloWorld!程式,我就照著寫了,測試了一下,可以顯示Hello World!,之後,上面說要加個getch()函數,按任意鍵後退出程式,我就改了一下源碼,加上標頭檔:conio.h,printf()後加上getch(),編譯之,結果可想而知,無法通過編譯,現在我是知道原因的,這個樣本源碼是適合windows系統的,而學習機是linux系統,與windows系統不同,有些標頭檔沒有。“趣味導學”裡面還有兩個源碼:在螢幕中移動的笑臉,推箱子遊戲;兩個都試過,由於編譯不通過就放棄了。

“編程天地”裡還有C語言教程,入門篇、初級篇、中級篇、進階篇,我都看過,折騰很久才會用printf()函數,什麼for、while、switch – case在那時都還不會用…

某天,沒事就研究學習機,但只會幾個命令,用ls命令查看了bin檔案夾裡的檔案,隨便選了一個,選到了gzip,加上“—help”參數,看了協助,英文的,還不理解,上網查了一下,gzip是個壓縮軟體,能將檔案壓縮成gz格式的壓縮檔,瞭解了這個後,又發現了tar和bzip2,經過測試,學習機裡有這些軟體,並且可用。但是,我發現敲命令列太麻煩,如果有個軟體能簡化操作就好了,於是就有了用C語言編寫壓縮管理程式的想法,從這個時候開始,“壓縮管理“便成為我的第一個正式的C語言項目,也是我學C語言的目的,為什麼要學C語言?學C語言幹什嗎?這兩個問題也就有了答案。

當時只會基本的函數: 用printf函數顯示字元菜單, scanf函數接受按鍵輸入,if語句判斷輸入內容,system函數調用命令進行操作,rename函數修改檔案名稱,列印的字元介面還是黑白的;

 

由於是上高中,每個月只有放月假才能回去碰電腦,其餘時間在學校,C語言代碼大部分是用學習機在課餘時間寫的,兩手握著學習機,瘋狂的點擊螢幕….

有時偶爾去網吧,用電腦敲代碼,當然,是代碼比較多、需要複製粘貼才去網吧的。

 

之前在學習機的論壇上見到某位高人的文章,他的程式能在終端列印彩色字元,在QQ上問了相關人士(學C的),無果,只好去百度,關鍵詞是:“linux 彩色 字元“,終於找到了答案:http://apps.hi.baidu.com/share/detail/24241711

不久,就將它應用了到我的程式,實現了彩色字元介面()。

這時的“壓縮管理“,功能很少,只支援指定位置裡的檔案的壓縮及解壓,想解壓/壓縮檔,就必須把檔案放到指定的檔案夾下,然後運行程式,進行操作。

按鍵輸入還是有毛病,不能一次性輸入多個字元,否則,會跳過菜單顯示,例如:主菜單有abcdef六個選項,每個選項都有子功能表,如果我輸入ab,那麼就會跳過a選項的子功能表的顯示,直接執行子功能表中的b選項操作。為瞭解決這個問題,我用了這麼個辦法:scanf(“%s”,&str);,if語句判斷,只判斷str[0]的值,每次輸入完後,清空str裡的全部內容。

 

“壓縮管理“完成後,想在論壇上發布,與其他機油分享成果,但又不想讓他們輕易獲得這個程式(畢竟是個人的勞動成果,有點捨不得),於是就想到了一個辦法:添加試用期,正版啟用。試用期為7天;

如何擷取時間?

毫無疑問,我去了CSDN論壇發帖,問了這個問題,文章:http://topic.csdn.net/u/20101016/09/13d6c4c1-8099-4fad-bc3f-7d80dc898117.html。

 

如何知道程式安裝時間?

我想, 程式第一次運行時建立一個檔案,記錄安裝時間,以後運行就讀取這個檔案,要實現這個,需要用到C語言的檔案操作類的函數,重新複習了一遍學習機裡內建的教程,又在網上搜尋了一番,我用了fprintf函數寫入資料至檔案,fscanf函數讀取檔案內容;

這時,我有了這麼一個想法:printf和fprintf都是輸出資料,用法基本一樣,前者是將內容輸出至螢幕,後者是輸出至檔案,fprintf前面的f是指file嗎?我想應該是的,那麼,是不是有個輸出至字串變數的函數?字串的名字是string, string+printf = sprint, 會有sprintf函數嗎?百度了一下,真的有,而且用法和printf、fprintf的基本一樣,這時,我便學會用sprintf函數改變字串變數的內容了,對以後的字串處理的協助很大。

安裝時間的儲存和讀取的功能已經基本實現,那如何知道還剩幾天呢?

如何判斷是否超過了試用期?

沒法,為了這個問題,有空就拿個草稿紙,用筆在上面畫程式運行流程圖,先幹什麼,後幹什麼,判斷什麼,畫著畫著,還是覺得直接敲代碼好一些;

判斷時間,我用了一堆if語句,先判斷年份,後判斷月份,天,小時,分鐘,秒;考慮到1月、3月、5月、7月、8月、10月、12月有31天,4月、6月、9月、11月有30天,而2月在平年有28天,閏年有29天,為了準確度,只好全部判斷了。(現在覺得這樣有點愚蠢)

試用期的天數的計算倒是勉強完成了,可是程式的正版啟用如何??

像電腦上的軟體那樣用CD-Key啟用?

CD-Key如何產生?

為了防止多個人共用一個CD-Key,如何?一個學習機上只能用專有的CD-Key啟用程式?

我是這樣想的:先實現獨一無二的產品ID的產生,之後,通過演算法算出對應的CD-Key,想啟用程式的機友,可將產品ID以回帖方式發給我,我再給他CD-Key,這樣就能增加文章的回複數和人氣,但是,這個功能由於本人當時技術水平有限,終究還是沒實現,關於這個問題,在論壇上提過:

http://topic.csdn.net/u/20101010/13/51fb341e-05c8-46f3-919c-6b17d3955d80.html

http://topic.csdn.net/u/20101210/09/1b49f8b8-213d-4c40-8b1d-c6e3015d3ea7.html

關於這個功能,先扯遠一點,某一天,在看include檔案夾的標頭檔時,發現了uuid,很想知道這個是幹什麼的,百度結果是:

UUID含義是通用唯一識別碼 (Universally Unique Identifier),這 是一個軟體建構的標準,也是被開源軟體基金會 (Open Software Foundation, OSF) 的組織在分散式運算環境 (Distributed Computing Environment, DCE) 領域的一部份。

研究了uuid.h標頭檔中的函數的用法,寫了個測試程式,經過多次修改,產生的數字可以以這種格式顯示:

xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx

這不正是我要找的能產生獨一無二的產品ID的功能嗎?但是,發現這個是很久以後的事了,那時可沒想過再添加這個功能。

迴歸正題:

當時,我不會寫自訂的函數,所有代碼都寫在main函數內,嘗試過把部分功能的代碼分離開,寫在自訂函數內,可是不知到該如何和main函數共用資料,例如:main函數將一個變數a作為參數傳給自訂的func函數,在func函數中可以把傳進來的變數的值隨意修改,可退出後,main函數的變數a還是沒變。這問題出現後,經過糾結和思考N天后,終於解決了,比如func函數原型是:func(char a[]);在a前面加個*,再去掉[]就可以了:func(char *a); ,func函數中的a變成了char型的指標,指向傳入的參數,對指標的內容進行修改,就相當於對調用func函數的上一級函數中的的變數進行修改。

上網搜尋過程中,還瞭解了局部變數和全域變數,全域變數可以多個函數共用,局部變數只在聲明它的函數內起作用。

代碼可以分離成多個函數,但還是在一個c檔案內,考慮到一個C檔案內原始碼量很多的話會影響編譯耗時,於是就想將代碼分離至多個檔案,這樣,編譯耗時就不會那麼長了,只重新編譯被修改過的C檔案中的原始碼。

小結:

通過編寫壓縮檔管理器,使我學會了:

基本輸入輸出函數scanf、printf、getchar的基本用法。

檔案操作函數fprintf、fscanf、fgets的基本用法,對於fwrite、fread這幾個函數的說明中提到的“資料區塊”一詞還不理解。

if-else語句的基本用法,以及常見類型變數的定義、變數的賦值。

自訂函數,以及基本的參數傳遞。

全域變數和局部變數的作用。

#define和#include的使用方法,還有簡單的標頭檔的編寫。

 

使用7-zip作為主要利用的程式

“壓縮管理”的第二個版本,也就是0.84版(之前的版本是測試版本),加入了很多新功能。那時,由於本人發現了一個強大的壓縮軟體:7-zip,就把它作為“壓縮管理“調用的主要程式,改用命令列版的7-zip,支援解壓的格式更多,壓縮檔的可自訂參數也較多,包括了系統內建的tar、gzip、bzip2這三個壓縮程式的功能;

 

為了更好的利用7-zip,我還特意研究了7-zip的命令列用法,花費了一些時間,終於實現了”壓縮管理“的壓縮參數設定功能,具體:

圖中所示的是7z格式的壓縮參數,更新後,又添加了幾個選項:

Zip格式的壓縮參數:

單詞大小、詞典大小、壓縮等級、壓縮演算法等參數均可自訂;7z和zip這兩種格式的壓縮參數最多,輸入對應字母即可切換參數,源碼中用的if語句也非常多,反正是根據自己的邏輯思維寫的,怎麼想的就用什麼樣的代碼描述。

這些自訂的參數都儲存在檔案內,參數儲存和讀取的功能費了比較大的功夫才完成的,實現這個功能,我是這樣想的:

首先,需要讀取每一行字串,這個,我在檔案操作類的函數中找到了fgets函數,它能讀取一行內容,要讓它讀取每一行內容,我就用了while迴圈,每讀一行就進行處理;那麼,如何搜尋已讀取的字串是否包含指定的字串?我用strstr函數,這個strstr函數是通過提問得到的:http://topic.csdn.net/u/20101111/09/927dccb8-0797-4e51-af0e-60dabe14f771.html

 

用strstr函數尋找是否有對應選項,有的話,儲存該選項後面的內容,例如:

有一行內容是這樣的:“switch = off“,程式搜尋時,就用strstr函數搜尋該行是否有“switch =“,有的話,判斷後面的內容是否正確,考慮到字串首尾會有空格,在網上找了個去除字串首尾空格的代碼,這個代碼是仿照trim函數寫的,又考慮到字元要不分大小寫對比,例如switch和SWITCH等同,找到了strcasecmp函數。

 

調用7-zip進行壓縮和解壓檔案主要是通過system函數實現的,命令列的內容,是通過sprintf函數處理產生的,先聲明一個char型的字串變數command用於儲存命令列內容,每個參數都有各自的變數,當這個參數有效時就賦值給相應變數,無效則清空相應變數裡的內容,最終用形如sprintf(command,”7z  %s %s %s%s %s %s %s”a,b,c,d,e,f,g)的代碼產生命令列,用system(command)運行命令列。

 

當用7-zip解壓和壓縮檔時,在終端上顯示的字元是英文的,於是就有了把它漢化的想法,於是,去看7-zip的原始碼,原始碼是C++語言編寫的,有些地方和C相似,還是能看明白一部分,找到需要漢化的字元,改成中文,黑白太單調,改用了彩色字元,重新編譯,這個編譯,是在學習機下完成的,先./configure,後make,這是在學習機論壇上得知的linux系統下編譯源碼的方法,受益匪淺啊。

下面是漢化後的,PC端ubuntu系統測試效果+PS合成,那時學習機出毛病送去修了,只有在電腦上測試,看字型應該看得出。

 

這個版本的“壓縮管理“還是不能添加任意位置的檔案壓縮成壓縮包,但是,卻可以解壓任意位置的壓縮檔;

研究了學習機內建的”資源管理“的關聯功能, 所有格式的檔案關聯資訊都儲存在resourcemanager.app檔案中,為了更好的修改檔案內容,我又用C語言編寫了一個”關聯管理“的程式,可以添加/編輯/刪除關聯資料,具體見,在此不做過多的說明。

 

為了更好的查看“壓縮管理“與支援的壓縮檔格式的關聯狀態,我就把”關聯管理“整合到了“壓縮管理“中,具體可以看圖:

主菜單中直接顯示已關聯的格式數量,新增的關聯管理功能菜單中可以顯示具體狀態,紅色背景的表示為已關聯,黃色字型表示未關聯。

 

由於壓縮7z和zip格式可以設定壓縮密碼,為了避免忘記密碼,加了密碼查詢功能,可以查詢之前設定的密碼;在操作完成時,程式會播放提示音,音頻播放的功能,我調用了madplay程式播放mp3格式的音頻。

 

 

學習機的總記憶體為64MB,平常的可用記憶體為5MB左右, 有時調用7zip壓縮檔和解壓檔案時會提示記憶體不夠,為了騰出足夠的記憶體空間供當前程式使用,需要一個SWAP分區來儲存暫時沒有用的程式的資料,於是就加了SWAP開關功能,SWAP這個東東也是在學習機論壇上得知的。

 

建立檔案關聯後,就可以在學習機裡的“資源管理”裡開啟壓縮檔,開啟檔案是調用“壓縮管理”,並將檔案的絕對路徑作為參數傳給它,int main(int argc,char *argv[])中的argc和argv就起到作用了,通過判斷argc的值得出是否有參數傳入,有參數傳入就會儲存在argv裡。

有參數時,會進入檔案操作菜單,這個菜單中的“位置”和“名稱”內容是變化的,內容過長、過短都會影響字元介面,於是,寫了個字串處理函數,指定字元的長度,低於這個長度則用空格填補,大於這個長度則截取內容,並將末尾3個字元改為”.”。

初始形態是這樣的:

可是,7-zip對壓縮檔的操作不止這些,花費了一些時間完善該功能的代碼,最終實現了如所示的效果:

傳入的參數是檔案的絕對路徑, 如何將檔案名稱和位置從絕對路徑中分離出來?當時採用的方法是計算絕對路徑中的“/”的個數,以最後一個”/”為字串截斷點,把字串分成兩個。

 

解壓壓縮檔時,可以進行設定,解壓後自動關閉終端,返回“資源管理”;

壓縮檔的釋放目錄可以自訂:目前的目錄、工作目錄、自訂目錄。

查看檔案清單是使用了“l”參數查看壓縮包內的檔案,用了輸出重新導向,把7z列印在螢幕的檔案清單重新導向至檔案。

壓縮檔還可以再次進行壓縮,考慮到tar格式的存在,就添加了這個功能, 將tar格式的檔案壓縮成gzip 、bzip2、zip、7z格式,可以看,在資源管理裡面,源檔案是terminfo.tar,7z和zip都是使用lzma演算法壓縮。

 

解壓和壓縮檔時,少不了檔案路徑的選擇,直接輸入字元很麻煩,就這樣,我又新增了直接選取功能,字元介面的:

但是,選擇的只是“本地磁碟”和“儲存卡”中固定的檔案目錄。

儲存卡的空間資訊顯示有誤,後來知道是塊大小的問題,我複製的原始碼中,塊大小直接用4,我改成了用stat結構體中的變數 st_blksize後,才正常計算了總空間大小。

 

曾經還用過Ncurses庫裡的函數實現程式啟動動畫,但是在學習機裡不支援彩色字元顯示,因為這個,就拋棄了Ncurses。

 

也記不起上面的圖片究竟是不是同一個版本的“壓縮管理”的,程式的更新比較頻繁。

剛開始寫C代碼時,聽說warning可以忽略,只重視error,偶然的機會,在網上發現了gcc編譯器的一個參數,-Wall開啟所有警告資訊,於是乎,用了這個參數,結果,發現我的程式原來有這麼多warning…

由於本人喜歡追求完美,想把所有warning消滅,花費了大量時間,百度了N次,最終還是消滅warning了,從此以後,寫的代碼編譯時間,都習慣性的給gcc編譯器加個-Wall參數,看看程式有什麼warning和error。

稍微提醒一下,大部分中能看到catchscreen  /mnt/xxxxx之類的字元,這個其實是我使用的命令,顯示介面後,立刻終止程式運行,並使用catchscreen命令。

有的人或許能從中發現一些細節:

在某些中的字元菜單的右下角多出了“[X]返回”、“[返回鍵]退出”的字元,其實,前期版本的“壓縮管理”的操作,需要靠輸入字元、然後按斷行符號才能確定操作的,而在我多次努力更新後,這種麻煩的操作已經被我拋棄,改用了直接按相應鍵進行操作,實現這個功能,主要是用了getch()函數;windows系統下有個getch()函數,可以無回顯、無需斷行符號即可接受按鍵輸入的索引值,在linux系統下雖然沒有,但可以類比實現getch()函數的功能,我百度了一番,關鍵詞是“linux getch
類比實現”,網上給的這個函數的原始碼,並不完美,不支援方向鍵,因為會把方向鍵分解成多個索引值,沒辦法,當時沒打算改進它,勉強的先用了。

到了“壓縮管理”的0.86版,添加了程式啟動畫面:

用的是QQ安裝目錄裡的部分映像素材,外加自己PS編輯。

有人可能會問,啟動畫面的顯示是如何?的?

顯示映像,我調用了mgaview程式,這個程式在學習機論壇上有機油發過,我也正好找到了它,可是呢,mgaview顯示的圖片只是臨時的,如果螢幕有變動,這圖片也就消失了,為了尋找解決方案,我就開始研究了mgaview的源碼,剛開始研究的並不深,只是把部分代碼改了一下,它的main函數我改成int view_image(char *filepath,int sec),傳入圖片檔案的路徑和顯示時間長度即可顯示,想把它編譯成一個庫,動態庫和靜態庫的編譯就是在這個時候開始學會的,我選擇了靜態庫,而靜態庫其實是多個o檔案經過打包而成的,用可用ar命令打包o檔案成靜態庫。

這個mgaview程式,是我的程式實現圖形化的一個重要的存在,如果沒有它,現在可能還是敲字元版的程式,在後面會提到它的。

0.86版的“壓縮管理”,功能已經比較完整,能添加的功能也盡量添加了,例如:IPK的安裝、IPK安裝包資訊的讀取、IPK安裝包的製作、IPK安裝資訊的讀取等等,IPK是學習機的安裝包檔案的一中格式,屬於壓縮檔,既然是壓縮檔,我的“壓縮管理”怎麼能放過呢?

從初始版本到0.86版,累計寫了3000+行的代碼,用行數表示還是不準確,0.83版(貌似是這個版本)的代碼量將近1000行,到了0.84版時,代碼重新開始寫,直到0.86版,一直在新增、修改代碼,0.85版的總原始碼的大小是126KB;到了0.86版,光是新增的7z和zip的壓縮參數配置功能的原始碼就佔了50KB左右,其它原始碼也經過修改和新增,由於現在只有0.87版的源碼,之前的源碼都找不到,0.86版之前的代碼量也就不知道是多少了。


小結:

在對壓縮檔管理器的版本進行更新的過程中,經曆了多次代碼新增、完善、最佳化,從中也學到了一些知識:

瞭解了7z、tar、gz、bzip2命令的部分參數的用法以及功能;

for、while迴圈的用法;

利用string.h標頭檔中聲明的函數,實現了基本的字串處理;

利用system函數實現程式的調用;設定檔的資料讀取與儲存;

檔案系統的空間資訊的擷取;

初步瞭解如何用pthred.h標頭檔中聲明的函數來實現多線程;

linux系統中有swap分區,和windows系統的虛擬記憶體的功能基本一樣;

gcc編譯器的-Wall參數對代碼文法問題的糾正起了很大作用,自從用了它後,我對我寫的程式的要求是無任何警告通過編譯;

以前只會gcc -c 編譯源碼,有些警告資訊被gcc隱藏了,雖然不是很嚴重的警告,但這也說明我寫的代碼還是存在一些語法錯誤;

學會自己通過百度Google搜尋問題解決方案,其實,學C語言是自己的事,需要自己獨立解決自己遇到的問題,不能依賴別人,簡單的語法錯誤和邏輯錯誤不要麻煩別人,在論壇上能討論的只是程式的設計思路、工作原理、效能的改善、代碼的最佳化;

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.