語言只是一種工具,任何語言之間都是相通的,一通則百通,關(guān)鍵是要理解語言背后的思想,理解其思想,任何語言,拿來用就行了。語言沒有好壞之分,任何語言既然存在自然有它存在的價值。 在一個到處是OOP的年代,為何面向過程的C語言依然可以如此活躍?這主要得益于C語言本身的語言特性。C語言小巧靈活,而且還有一個直接與硬件打交道的指針的存在,所以它是嵌入式開發(fā)唯有的高級語言;正因為他的小巧靈活,我們可以用它來開發(fā)一系列的小工具,Unix/Linux就是由這些小工具組成的操作系統(tǒng);同時用C語言可以開發(fā)高性能的應(yīng)用程序。 1、數(shù)據(jù)類型。C是一門面向過程的語言,但它依舊可以實現(xiàn)大多數(shù)面向?qū)ο笏芡瓿傻墓ぷ?。比如面向?qū)ο蟮娜筇匦裕悍庋b、繼承、多態(tài)。 封裝:C中有一種復(fù)雜的數(shù)據(jù)結(jié)構(gòu)叫做struct。struct是C里面的結(jié)構(gòu)體。 假如我們要對person進行封裝,person可能包括姓名、性別、年齡、身高、體重等信息。我們就可以對它封裝如下: struct Person{ 當我們要像OOP那樣新建一個對象時,我們就可以: struct Person p; 我們就可以直接對p進行賦值: p.name = "whc"; 繼承:同樣利用struct,我們來創(chuàng)建一個學生結(jié)構(gòu),同時繼承結(jié)構(gòu)體Person,如下: struct Student{ 對Student進行創(chuàng)建對象,并賦值: struct Student s; 多態(tài):C中對于多態(tài)的實現(xiàn)可以借助函數(shù)指針來實現(xiàn)。為了簡單起見,我們假設(shè)Person這個結(jié)構(gòu)體中,只有一個函數(shù)指針。 struct Person{ struct Student{ 而Person和Student這兩個結(jié)構(gòu)體的print函數(shù)實現(xiàn)如下: void printPerson(void *person){ 我們寫一個函數(shù)來調(diào)用他們: void print(void *person){ print(&person); //實參為Person的對象 return 0; 他們的輸出為: 其實這個也不難理解,無論是Person還是Student,他們在內(nèi)存中只有一個變量,就是那個函數(shù)指針,而void*表示任何類型的指針,當我們將它強制轉(zhuǎn)換成struct Person*類型時,p->print指向的自然就是傳入實參的print地址。 2、 指針和內(nèi)存管理 無論問哪一個C工程獅:C語言中最容易出錯的地方在哪?我們基本上會得到同一個答案,那就是指針和內(nèi)存溢出。那么指針是什么,指針其實就是一個地址,這個地址可以是一個變量的地址,也可以是一個函數(shù)的地址,不管是什么,反正都是內(nèi)存中的一個地址。 例如有一個變量a,我們定義一個指針來保存變量a的地址: int a = 0; 如果是一個函數(shù)呢?我們定義一個函數(shù),然后用一個函數(shù)指針來保存這個函數(shù)地址: int min(int a,int b){ int (*f)(int,int); 可能我們有時候會想,難道我們只能先定義一個變量或者函數(shù),然后把它的地址給指針么?不能直接使用指針,或者直接給指針賦一個常量么?首先,我們不知道內(nèi)存中哪些是可用的地址,哪些是不可用的,每當我們定義一個指針時,這個指針指向的是一個未定義的內(nèi)存,這個就是傳說中的野指針。如果我們給這個指針所指向的內(nèi)存賦值,就有可能覆蓋了一些很重要的數(shù)據(jù),所以每當我們定義一個指針時,最好給它賦一個初始地址或者NULL;如果我們給一個指針賦常量,同樣的道理。 指針的類型要與變量的類型一致(如果我們不是故意要他們不一致),所謂類型,只是變量的一直表現(xiàn)形式,其實在內(nèi)存中,他們不過是0101的二進制,當我們用32bits的原碼表示時,它就是unsigned;當我們用32bits補碼表示時,就是signed;當用浮點表示時就是float;當用更復(fù)雜的自定義表示時就是struct;用union可以很好的理解這些。 現(xiàn)在我們來講一下內(nèi)存,這里我們只討論用戶內(nèi)存區(qū)域: 一般分為5個區(qū)域: (1)程序代碼區(qū):存放代碼指令的地方 (2)全局(靜態(tài))變量區(qū):包括初始化、未初始化的全局變量和靜態(tài)變量 (3)字符常量區(qū):存放一些字符串常量,在C語言里面,這個很容易與棧中定義的字符數(shù)組搞混,當我們定義如下: int main(){ str0所指向的字符串就是在字符常量區(qū),但是str0本身的這個指針變量是在棧區(qū)的,這個變量存放的是字符常量區(qū)中"Hello World!"的首地址。 str1是字符數(shù)組,所以str1中所存放的字符串是在棧區(qū),這里利用的不過是字符數(shù)組初始化的一種形式,其實它可以寫成如下形式: char str1[] = {'H','e','l','l','o',' ','W','o','r','l','d','!','\0'}; (4)棧區(qū):局部變量,形參,函數(shù)返回地址等,由系統(tǒng)來管理,在內(nèi)存里面是由高地址往低地址生長,所以??臻g大小是有限的,當在棧中定義一個很大的數(shù)組或者使用很深的遞歸調(diào)用時,就有可能棧溢出。 (5)堆區(qū):由malloc、calloc、realloc函數(shù)分配的空間,由我們自己來管理,每次用完之后,必須用free釋放內(nèi)存,否則,就會產(chǎn)生內(nèi)存泄漏,每次釋放內(nèi)存后,雖然不再占用著這塊內(nèi)存中,但是對應(yīng)的指針依然指向這塊區(qū)域,這個指針就是野指針,所以釋放內(nèi)存后,建議給指針賦NULL。如下: int main(){ 3、C語言的I/O輸入輸出 C語言本身并不帶有輸入輸出的特性,所以它的所有I/O操作都是通過系統(tǒng)調(diào)用來實現(xiàn)。幸運的是C標準庫,已經(jīng)給我們封裝好了一系列的I/O操作的函數(shù)。 putchar ():把變量中的一個字符常量輸出到顯示器屏幕上; getchar ();從鍵盤上輸入一個字符常量,此常量就是該函數(shù)的值; printf ();把鍵盤中的各類數(shù)據(jù),加以格式控制輸出到顯示器屏幕上; scanf ();從鍵盤上輸入各類數(shù)據(jù),并存放到程序變量中; puts ():把數(shù)組變量中的一個字符串常量輸出到顯示器屏幕上 gets ():從鍵盤上輸入一個字符串常量并放到程序的數(shù)組中 一些為對文件的操作,由于一切皆可看作是文件,標準輸入,輸出也可以當作文件來操作,文件描述符:標準輸入(0)、標準輸出(1)、標準錯誤(2) fputs();輸出到文件 fgets();從文件輸入 fscanf();格式化文件輸入 fprintf();格式化文件輸出 另外兩個很重要的函數(shù),當然還有他們的派生函數(shù)也是類似的 sscanf(); 從一個字符串中提取各類數(shù)據(jù)。 sprintf(); 把格式化的數(shù)據(jù)寫入某個字符串 這里不對每個函數(shù)進行詳解,主要對格式化函數(shù)進行分析: (1)當我們要把一個字符串轉(zhuǎn)換成一個整數(shù)或者把一個整數(shù)轉(zhuǎn)換成一個字符串時,我們一般會想到atoi()或者itoa()(非標準函數(shù)),但是我們可以通過流來實現(xiàn): int main(){ return 0; 輸出結(jié)果如下: 把字符串轉(zhuǎn)與其它類型之間的轉(zhuǎn)換:比如float,16進制,unsigned等都可以用流實現(xiàn)。 (2)格式化函數(shù)中的正則表達式 所有的格式化函數(shù)都可以定制自己的掃描集 %[abc]、%[a-z]、%[^abc]、%[^a-z],其中[]內(nèi)是匹配的字符,^表示求反集。 當我們要從標準輸入輸入一個可能帶空格的字符串時,直接用scanf("%s",str);當讀到空格時就返回,此時就可以使用正則表達式: char str[100] = {0}; 從標準輸入中只要讀小寫字母a-z,遇到其它字符則返回: char str[100] = {0}; 其他格式化函數(shù)的用法相同,不一???舉例。 4、總結(jié) 從大一開始學習C語言也有四五年了,個人認為:C語言中最大的成功在于它的指針,但是也是最容易出錯的,想要理解C,必須要掌握指針。雖然說,語言只是一門工具,但是這是基礎(chǔ)?;蛟S,你可以說,現(xiàn)在是JAVA的天下了,滿大街都是招聘JAVA工程師;或者你可以說C太底層,現(xiàn)在都是OOP的時代了,誰還會用面向過程的......你們不要忘了操作系統(tǒng)是用什么寫的?是C;C實現(xiàn)的nginx的并發(fā)量是C++實現(xiàn)的apache的幾十倍。無論是什么編程語言,好好學,深入學就行,不要因為它今天流行就拋棄昨天所學的。 本文永久更新鏈接地址:http://www./Linux/2015-08/121239.htm |
|
來自: cuibaofeng > 《Linux C》