久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

深入理解C語言中的指針與數(shù)組之指針篇

 荔枝園子 2016-07-26

前言

         其實(shí)很早就想要寫一篇關(guān)于指針和數(shù)組的文章,,畢竟可以認(rèn)為這是C語言的根本所在,。相信,任意一家公司如果想要考察一個人對C語言的理解,,指針和數(shù)組絕對是必考的一部分,。

         但是之前一方面之前一直在忙各種事情,一直沒有時間靜下心來寫這些東西,,畢竟這確實(shí)是一件非常耗費(fèi)時間和精力的事情,;一方面,個人對C語言的掌握和理解也還有限,,怕寫出來的東西會對大家造成誤導(dǎo),。當(dāng)然,,今天寫的這些東西也肯定存在各種問題,不嚴(yán)謹(jǐn)甚至錯誤的地方肯定有,,也希望大家來共同探討,,相互改進(jìn)。

         我會慢慢的寫完這幾章,,有想法的童鞋可以和我探討,。


指針


預(yù)備知識

         在深入理解指針之前,我認(rèn)為有必要先復(fù)習(xí)或者學(xué)習(xí)一下計算機(jī)原理的基礎(chǔ)知識,。 

計算機(jī)是如何從內(nèi)存中進(jìn)行取指的,?

         計算機(jī)的總線可以分為3種:數(shù)據(jù)總線,地址總線和控制總線,。這里不對控制總線進(jìn)行描述,。數(shù)據(jù)總線用于進(jìn)行數(shù)據(jù)信息傳送。數(shù)據(jù)總線的位數(shù)一般與CPU的字長一致,。一般而言,,數(shù)據(jù)總線的位數(shù)跟當(dāng)前機(jī)器int值的長度相等。例如在16位機(jī)器上,,int的長度是16bit,,32位機(jī)器則是32bit。這個計算機(jī)一條指令最多能夠讀取或者存取的數(shù)據(jù)長度,。大于這個值,,計算機(jī)將進(jìn)行多次訪問。這也就是我們說的64位機(jī)器進(jìn)行64位數(shù)據(jù)運(yùn)算的效率比32位要高的原因,,因為32位機(jī)要進(jìn)行兩次取指和運(yùn)行,,而64位機(jī)卻只需要一次!

         地址總線專門用于尋址,,CPU通過該地址進(jìn)行數(shù)據(jù)的訪問,,然后把處于該地址處的數(shù)據(jù)通過數(shù)據(jù)總線進(jìn)行傳送,傳送的長度就是數(shù)據(jù)總線的位數(shù),。地址總線的位數(shù)決定了CPU可直接尋址的內(nèi)存空間大小,,比如CPU總線長32位,其最大的直接尋址空間長232KB,,也就是4G,。這也就是我們常說的32位CPU最大支持的內(nèi)存上限為4G(當(dāng)然,實(shí)際上支持不到這個值,,因為一部分尋址空間會被映射到外部的一些IO設(shè)備和虛擬內(nèi)存上?,F(xiàn)在通過一些新的技術(shù),可以使32位機(jī)支持4G以上內(nèi)存,但這個不在這里的討論范圍內(nèi)),。

         一般而言,,計算機(jī)的地址總線和數(shù)據(jù)總線的寬度是一樣的,我們說32位的CPU,,數(shù)據(jù)總線和地址總線的寬度都是32位。

         計算機(jī)訪問某個數(shù)據(jù)的時候,,首先要通過地址總線傳送數(shù)據(jù)存儲或者讀取的位置,,然后在通過數(shù)據(jù)總線傳送需要存儲或者讀取的數(shù)據(jù)。一般地,,int整型的位數(shù)等于數(shù)據(jù)總線的寬度,,指針的位數(shù)等于地址總線的寬度。

 

計算機(jī)的基本訪問單元

         學(xué)過C語言的人都知道,,C語言的基本數(shù)據(jù)類型中,,就屬char的位數(shù)最小,是8位,。我們可以認(rèn)為計算機(jī)以8位,,即1個字節(jié)為基本訪問單元。小于一個字節(jié)的數(shù)據(jù),,必須通過位操作來進(jìn)行訪問,。

 

內(nèi)存訪問方式

         如圖1所示,計算機(jī)在進(jìn)行數(shù)據(jù)訪問的時候,,是以字節(jié)為基本單元進(jìn)行訪問的,,所以可以認(rèn)為,計算每次都是從第p個字節(jié)開始訪問的,。訪問的長度將由編譯器根據(jù)實(shí)際類型進(jìn)行計算,,這在后面將會進(jìn)行講述。


圖1 內(nèi)存訪問方式

 

         想要了解更多,,就去翻閱計算機(jī)組成原理和編譯原理吧,。


sizeof關(guān)鍵字

         sizeof關(guān)鍵字是編譯器用來計算某些類型的數(shù)據(jù)的長度的,以字節(jié)為基本單位,。例如:

         sizeof(char)=1;

         sizeof(int)=4;

         sizeof(Type)的值是在編譯的時候就計算出來了的,,可以認(rèn)為這是一個常量!


什么是指針

         指針其實(shí)就是數(shù)據(jù)存放的地址,,圖1中的p就是一個指針,。在圖1中,n一般是CPU的位數(shù),,32位機(jī)上,,n=32。因為指針需要能夠指向內(nèi)存中的任意一個位置,因此,,指針的長度應(yīng)該是n位的,,32位機(jī)器上指針長度就是32位。這和整型的長度是相等的,!

         在我個人的理解中,,可以將指針理解成int整型,只不過它存放的數(shù)據(jù)是內(nèi)存地址,,而不是普通數(shù)據(jù),我們通過這個地址值進(jìn)行數(shù)據(jù)的訪問,,假設(shè)它的是p,,意思就是該數(shù)據(jù)存放位置為內(nèi)存的第p個字節(jié)。

         當(dāng)然,,我們不能像對int類型的數(shù)據(jù)那樣進(jìn)行各種加減乘除操作,,這是編譯器不允許的,因為這樣錯是非常危險的,!

         圖2就是對指針的描述,,指針的值是數(shù)據(jù)存放地址,因此,,我們說,,指針指向數(shù)據(jù)的存放位置。


圖2 指針


指針的長度

         我們使用這樣的方式來定義一個指針:

         Type *p;

         我們說p是指向type類型的指針,,type可以是任意類型,,除了可以是char,short, int, long等基本類型外,還可以是指針類型,,例如int *, int **, 或者更多級的指針,,也可是是結(jié)構(gòu)體,類或者函數(shù)等,。于是,,我們說:

         int * 是指向int類型的指針,;

         int **,也即(int *) *,是指向int *類型的指針,,也就是指向指針的指針,;

         int ***,,也即(int **) *,,是指向int**類型的指針,也就是指向指針的指針的指針,;

         …我想你應(yīng)該懂了

         struct xxx *,,是指向struct xxx類型的指針,;

         其實(shí),說這么多,,只是希望大家在看到指針的時候,,不要被int ***這樣的東西嚇到,就像前面說的,,指針就是指向某種類型的指針,,我們只看最后一個*號,前面的只不過是type類型罷了,。

         細(xì)心一點(diǎn)的人應(yīng)該發(fā)現(xiàn)了,,在“什么是指針”這一小節(jié)當(dāng)中,已經(jīng)表明了:指針的長度跟CPU的位數(shù)相等,,大部分的CPU是32位的,因此我們說,,指針的長度是32bit,,也就是4個字節(jié)!注意:任意指針的長度都是4個字節(jié),,不管是什么指針?。ó?dāng)然64位機(jī)自己去測一下,應(yīng)該是8個字節(jié)吧,。,。。)

         于是:

         Type *p;

         sizeof(p)的值是4,,Type可以是任意類型,,char,int, long, struct, class, int **…

         以后大家看到什么sizeof(char*), sizeof(int *),sizeof(xxx *),,不要理會,,統(tǒng)統(tǒng)寫4,只要是指針,,長度就是4個字節(jié),,絕對不要被type類型迷惑!至于type是干什么用的,,這個是給編譯器用的,,用于指針運(yùn)算,這個在下面的章節(jié)中會有詳細(xì)介紹,。


取地址

         我們說指針指向的是數(shù)據(jù)的存放地址,,因此指針的值等于數(shù)據(jù)的存放地址。那么給指針賦值的時候就需要進(jìn)行數(shù)據(jù)的取地址操作,,這個我想不用我多說,,各位也知道是&符號,,沒錯,是&符號,。

         我們可以這樣取地址:

         Type v,*p=&v;

         當(dāng)然也可以:

         Type v, *p;(或者Type v; Type *p)

         p=&v;

         這里的Type依然是任意的類型,,可以是N級指針、結(jié)構(gòu)體,、類或者函數(shù)什么的,。


指針運(yùn)算

         N多的面試會考這種東西了:

         Type *p;

         p++;

         然后問你p的值變化了多少。

         其實(shí),,也可以認(rèn)為這是在考編譯器的基本知識,。因此p的值并不像表面看到的+1那么簡單,編譯器實(shí)際上對p進(jìn)行的是加sizeof(Type)的操作,。

 

         看一個一段代碼的測試結(jié)果:


[cpp] view plaincopy

  1. char cv='a',*pcv=&cv;  

  2. short sv=1, *psv=&sv;  

  3. int iv=1, *piv=&iv;  

  4. long lv=1, *plv=&lv;  

  5. long long llv=1, *pllv=&llv;  

  6. float fv=1.0, *pfv=&fv;  

  7. double dv=1.0, *pdv=&dv;  

  8. long double ldv=1.0, *pldv=&ldv;  

  9.    

  10. //cout<<"pcv:"<<pcv<<" pcv+1: "<<pcv+1<<endl;  

  11. cout<<"psv:"<<psv<<" psv+1: "<<psv+1<<endl;  

  12. cout<<"piv:"<<piv<<" piv+1: "<<piv+1<<endl;  

  13. cout<<"plv:"<<plv<<" plv+1: "<<plv+1<<endl;  

  14. cout<<"pllv:"<<pllv<<" pllv+1: "<<pllv+1<<endl;  

  15. cout<<"pfv:"<<pfv<<" pfv+1: "<<pfv+1<<endl;  

  16. cout<<"pdv:"<<pdv<<" pdv+1: "<<pdv+1<<endl;  

  17. cout<<"pldv:"<<pldv<<" pldv+1: "<<pldv+1<<endl;  

  18. cout<<endl;  



         (這里注釋掉char一行的原因是因為cout<<(char*)會被當(dāng)成字符串輸出,,而不是char的地址)

         執(zhí)行結(jié)果:

         

         觀察結(jié)果,可以看出,,他們的增長結(jié)果分別是:

         2(sizeof(short))

         4(sizeof(int))

         4(sizeof(long))

         8(sizeof(long long))

         4(sizeof(float))

         8(sizeof(double))

         12(sizeof(long double))

         喏,,增加的值是不是sizeof(Type)呢?別的什么struct,,class之類的,,就不驗證你,有興趣的自己去驗證,。

 

         我們再對這樣的一段代碼進(jìn)行匯編,,查看編譯器是如何進(jìn)行指針的加法操作的:


[cpp] view plaincopy

  1. int iv=1,*piv=&iv;  

  2. piv++;  

  3. cout<<piv<<endl;  

  4. piv=piv+4;  

  5. cout<<piv<<endl;  

  6. cout<<endl;  



         匯編結(jié)果:


[cpp] view plaincopy

  1. call    ___main  

  2. movl    $1, -12(%ebp)  

  3. leal    -12(%ebp), %eax  

  4. movl    %eax, -8(%ebp)  

  5. addl    $4, -8(%ebp)  /** 這里是piv++ **/  

  6. movl    -8(%ebp), %eax  

  7. movl    %eax, 4(%esp)  

  8. movl    $__ZSt4cout, (%esp)  

  9. call    __ZNSolsEPKv  

  10. movl    $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp)  

  11. movl    %eax, (%esp)  

  12. call    __ZNSolsEPFRSoS_E  

  13. addl    $16, -8(%ebp)  /** 這里是piv+4 **/  

  14. movl    -8(%ebp), %eax  

  15. movl    %eax, 4(%esp)  

  16. movl    $__ZSt4cout, (%esp)  

  17. call    __ZNSolsEPKv  

  18. movl    $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp)  

  19. movl    %eax, (%esp)  

  20. call    __ZNSolsEPFRSoS_E  

  21. movl    $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp)  

  22. movl    $__ZSt4cout, (%esp)  

  23. call    __ZNSolsEPFRSoS_E  

  24. movl    $0, %eax  

  25. addl    $36, %esp  

  26. popl    %ecx  

  27. popl    %ebp  

  28. leal    -4(%ecx), %esp  

  29. ret  


          注意看注釋部分的結(jié)果,我們看到,,piv的值顯示加了4(sizeof(int)),,然后又加了16(4*sizeof(int))。

         總結(jié)一點(diǎn):

         指針的實(shí)際運(yùn)算,,將會由編譯器在編譯的時候,,根據(jù)指針指向數(shù)據(jù)類型的大小進(jìn)行實(shí)際的翻譯轉(zhuǎn)換。指針類型的作用就在于此,,讓編譯器能夠正確的翻譯這些指令的操作,,另一方面,也讓編譯器檢查程序員對指針的操作是否合法,,保證程序的正確性和健壯性,。

         Type *p;  p=p+i;

         最終p的值實(shí)際上是(value of p) + i*sizeof(Type);

         Type *p;  p=p-i;

         最終p的值實(shí)際上是(value of p) - i*sizeof(Type),;

         注意:指針只能進(jìn)行加法和減法操作,,不能進(jìn)行乘除法!(指針畢竟不是普通的整數(shù),,乘除法的跨度太大了,,出發(fā)還會搞出小數(shù)點(diǎn)神馬的,,這是我個人的理解。但是編譯器不允許進(jìn)行指針的乘除法,。)


NULL指針

         NULL是C語言標(biāo)準(zhǔn)定義的一個值,,這個值其實(shí)就是0,只不過為了使得看起來更加具有意義,,才定義了這樣的一個宏,,中文的意思是空,表明不指向任何東西,。你懂得,。不過這里不討論空和零的區(qū)別,呵呵,。

         在C語言中,,NULL其實(shí)就是0,就像前面說的指針可以理解成特殊的int,,它總是有值的,,p=NULL,其實(shí)就是p的值等于0,。對于不多數(shù)機(jī)器而言,0地址是不能直接訪問的,,設(shè)置為0,,就表示該指針哪里都沒指向。

         當(dāng)然,,就機(jī)器內(nèi)部而言,,NULL指針的實(shí)際值可能與此不同,這種情況下,,編譯器將負(fù)責(zé)零值和內(nèi)部值之間的翻譯轉(zhuǎn)換,。

         NULL指針的概念非常有用,它給了你一種方法,,表示某個特定的指針目前并未指向任何東西,。例如,一個用于在某個數(shù)組中查找某個特定值的函數(shù)可能返回一個指向查找到的數(shù)組元素的指針,。如果沒找到,,則返回一個NULL指針。

         在內(nèi)存的動態(tài)分配上,,NULL的意義非同凡響,,我們使用它來避免內(nèi)存被多次釋放,造成經(jīng)常性的段錯誤(segmentation fault),。一般,,在free或者delete掉動態(tài)分配的內(nèi)存后,,都應(yīng)該立即把指針置空,避免出現(xiàn)所以的懸掛指針,,致使出現(xiàn)各種內(nèi)存錯誤,!例如:


[cpp] view plaincopy

  1. int *p=(int*)malloc(sizeof(int));  

  2. *p=23;  

  3. free(p);  

  4. p=NULL;  


         free函數(shù)是不會也不可能把p置空的。像下面這樣的代碼就會出現(xiàn)內(nèi)存段錯誤:


[cpp] view plaincopy

  1. int *p=(int*)malloc(sizeof(int));  

  2. *p=23;  

  3. free(p);  

  4. free(p);  


         因為,,第一次free操作之后,,p指向的內(nèi)存已經(jīng)釋放了,但是p的值還沒有變化,,free函數(shù)改不了這個值,,再free一次的時候,p指向的內(nèi)存區(qū)域已經(jīng)被釋放了,,這個地址已經(jīng)變成了非法地址,,這個操作將導(dǎo)致段錯誤的發(fā)生(此時,p指向的區(qū)域剛好又被分配出去了,,但是這種概率非常低,,而且對這樣一塊內(nèi)存區(qū)域進(jìn)行操作是非常危險的!)

但是下面這段代碼就不會出現(xiàn)這樣的問題:


[cpp] view plaincopy

  1. int *p=(int*)malloc(sizeof(int));  

  2. *p=23;  

  3. free(p);  

  4. p=NULL;  

  5. free(p);  


         因為p的值編程了NULL,,free函數(shù)檢測到p為NULL,,會直接返回,而不會發(fā)生錯誤,。

         這里順便告訴大家一個內(nèi)存釋放的小竅門,,可以有效的避免因為忘記對指針進(jìn)行置空而出現(xiàn)各種內(nèi)存問題。這個方法就是自定義一個內(nèi)存釋放函數(shù),,但是傳入的參數(shù)不知指針,,而是指針的地址,在這個函數(shù)里面置空,,如下:


[cpp] view plaincopy

  1. #include<iostream>  

  2. #include<stdlib.h>  

  3. using namespace std;  

  4.   

  5. void my_free(void *p){  

  6.     void **tp=(void **)p;  

  7.     if(NULL==*tp)  

  8.       return ;  

  9.     free(*tp);  

  10.     *tp=NULL;  

  11. }  

  12.   

  13. int main(int argc, char **argv){  

  14.      int *p=new int;  

  15.      *p=1;  

  16.      cout<<p<<endl;  

  17.      my_free(&p);  

  18.      cout<<p<<endl;  

  19.      free(p);  

  20.      return 0;  

  21.  }  




         結(jié)果:

         

         my_free調(diào)用了之后,,p的值就變成了0(NULL),調(diào)用多少次free都不會報錯了,!

         另外一個方式也非常有效,,那就是定義FREE宏,在宏里面對他進(jìn)行置空,。例如


[cpp] view plaincopy

  1. #include<iostream>  

  2. #include<stdlib.h>  

  3. using namespacestd;  

  4.    

  5. #define FREE(x) if(x) free(x);  x=NULL  

  6.    

  7. int main(intargc, char **argv){  

  8.     int *p=new int;  

  9.     *p=1;  

  10.     cout<<p<<endl;  

  11.     FREE(p);  

  12.     cout<<p<<endl;  

  13.     free(p);  

  14.     return 0;  

  15. }  


         執(zhí)行結(jié)果同上面一樣,,不會報段錯誤:

          

         (關(guān)于內(nèi)存的動態(tài)分配,這是個比較復(fù)雜的話題,,有機(jī)會再專門開辟一章給各位講述一下吧,,寫個帖子還是很花費(fèi)時間和精力的,呵呵,,寫過的童鞋應(yīng)該都很清楚,,所以順便插一句,,轉(zhuǎn)帖可以,請注明出處,,畢竟,,大家都是本著共享的精神來討論問題的,寫的好壞都沒有向你所要什么,,請尊重每個人的勞動成果,。)


void指針

         雖然從字面上看,void的意思是空,,但是void指針的意思,,可不是空指針的意思,空指針指的是上面所說的NULL指針,。

         void指針實(shí)際上的意思是指向任意類型的指針,。任意類型的指針都可以直接賦給void指針,而不需要進(jìn)行強(qiáng)制轉(zhuǎn)換,。

         例如:

         Type a, *p=&a;(Type等于char, int, struct, int *…)

         void *pv;

         pv=p;

         就像前面說的,,void指針的好處,就在于,,任意的指針都可以直接賦值給它,,這在某些場合非常有用,因此有些操作對于任意指針都是相同的,。void指針最常用于內(nèi)存管理,。最典型的,也是大家最熟知的,,就是標(biāo)準(zhǔn)庫的free函數(shù)。它的原型如下:

         void free(void*ptr);

         free函數(shù)的參數(shù)可以是任意指針,,沒有誰見過free參數(shù)里面的指針需要強(qiáng)壯為void*的吧,?

 

         malloc, calloc,realloc這些函數(shù)的返回值也是void指針,因為內(nèi)存分配,,實(shí)際上只需要知道分配的大小,,然后返回新分配內(nèi)存的地址就可以了,指針的值就是地址,,返回的不管是何種指針,,其實(shí)結(jié)果都是一樣的,因為所有的指針長度其實(shí)都是32位的(32位機(jī)器),,它的值就是內(nèi)存的地址,,指針類型只是給編譯器看的,目的是讓編譯器在編譯的時候能夠正確的設(shè)置指針的值(參見指針運(yùn)算章節(jié)),。如果malloc函數(shù)設(shè)置成下面這樣的原型,,完全沒有問題,。

         char*malloc(size_t sz);

         實(shí)際上設(shè)置成

         Type*malloc(size_t sz);

         也是完全正確的,使用void指針的原因,,實(shí)際上就像前面說的,,void指針意思是任意指針,這樣設(shè)計更加嚴(yán)謹(jǐn)一些,,也更符合我們的直觀理解,。如果對前面我說的指針概念理解的童鞋,肯定明白這一點(diǎn),。


未初始化和非法指針

         經(jīng)常有面試,,會考這樣的代碼校錯:

         int *a;

         …

         *a=12;

         這段代碼,在*a=12這里出了問題,。這里的問題就在于,,a究竟指向哪里?我們聲明了這個變量,,但是從未對它進(jìn)行初始化,,一般而言,沒有初始化,,a的值是任意的,,隨機(jī)的。如果a是全局變量或者static類型,,它會被初始化為0(前面說過,,其實(shí)指針可以理解成值是內(nèi)存地址的int),但是不管哪種方式,,這種方式的賦值都是非常危險的,,如果你有著中體彩頭號彩票的運(yùn)氣,a的值剛好等于某個變量或者分配內(nèi)存的地址,,那么這里的運(yùn)行不會報錯,,但這時候的運(yùn)氣卻不是什么好運(yùn),相反,,是非常倒霉,!因為這是對一塊不屬于你的內(nèi)存進(jìn)行操作,這實(shí)在是太危險了,!如果a的初始值是個非法地址,,這個賦值語句在執(zhí)行的時候?qū)箦e,從而終止程序嗎,,這個錯誤同樣是段錯誤(segmentation fault),,如果是這樣,你是幸運(yùn)的,因為你發(fā)現(xiàn)了它,,這樣就可以修正它,。

         關(guān)于這種問題,編譯器可能會,,也可能不會對它進(jìn)行檢測,。GNU的編譯器是會進(jìn)行檢測的,會對未初始化的指針或變量輸出警告信息,。


多級指針(也叫指針的指針)

         其實(shí)如果對前面的指針概念完全理解了,,這里都可以略過。指針的指針,,無非就是指針指向的數(shù)據(jù)類型是指針罷了,。

         Type *p;

         其中Type類型是指針,比如可以是int*,,也可以是int **,這樣p對應(yīng)的就是二級指針和三級指針,。一級指針的值存放的是數(shù)據(jù)的地址,二級指針的值存放的一級指針的地址,,三級指針的值存放的是二級指針的地址,,依此類推…


函數(shù)指針

         跟普通的變量一樣,每一個函數(shù)都是有其地址的,,我們通過跳轉(zhuǎn)到這個地址執(zhí)行代碼來進(jìn)行函數(shù)調(diào)用,,只是,跟取普通數(shù)據(jù)不同的在于,,函數(shù)有參數(shù)和返回值,,在進(jìn)行函數(shù)調(diào)用的時候,首先需要將參數(shù)壓入棧中,,調(diào)用完成后又需要將參數(shù)壓入棧中,。既然函數(shù)也是通過地址來進(jìn)行訪問的,那它也可以使用指針來指向,,事實(shí)上,,每一個函數(shù)名都是一個指針,不過它是指針常量和指針常量,,它的值是不能改的,指向的值也不能改,。

         (關(guān)于常量指針和指針常量什么的,,有時間在專門開辟一章來說明const這個東東吧,也是很有講頭的一個東東,。,。。)

 

         函數(shù)指針一般用來干什么呢,?函數(shù)指針最常用的場合就是回調(diào)函數(shù),。回調(diào)函數(shù),,顧名思義,就是某個函數(shù)會在適當(dāng)?shù)臅r候被別人調(diào)用,。當(dāng)期望你調(diào)用的函數(shù)能夠使用你的某些方式去操作的時候,,回調(diào)函數(shù)就很有用,比如,,你期望某個排序函數(shù)在比較的時候,,能夠使用你定義的比較方法去比較。

         有過較深入的C編程經(jīng)驗的人應(yīng)該都接觸過,。C的標(biāo)準(zhǔn)庫中就有使用,,例如在strlib.h頭文件的qsort函數(shù),它的原型為:

         void qsort(void*__base, size_t __nmemb, size_t __size, int(*_compar)(const void *, const void*));

         其中int(*_compar)(const void *, const void *)就是回調(diào)函數(shù),,這個函數(shù)用于qsort函數(shù)用于數(shù)據(jù)的比較,。下面,我會舉一個例子,,來描述qsort函數(shù)的工作原理,。

         一般,我們使用下面這樣的方式來定義函數(shù)指針:

         typedef int(*compare)(const void *x, const void *y);

         這個時候,,compare就是參數(shù)為const void *, const void *類型,,返回值是int類型的函數(shù)。例如:


[cpp] view plaincopy

  1. typedef int (*compare)(const void *x, const void *y);  

  2.   

  3. int my_compare(const void *x, const void *y){  

  4.     const int *a=(int *)x;  

  5.     const int *b=(int *)y;  

  6.     if(*a>*b)  

  7.       return 1;  

  8.     if(*a==*b)  

  9.       return 0;  

  10.     return -1;  

  11. }  

  12.   

  13. void my_sort(void *data, int length, int size, compare){  

  14.     char *d1,*d2;  

  15.     //do something  

  16.     if(compare(d1,d2)<0){  

  17.       //do something  

  18.     }else if(compare(d1,d2)==0){  

  19.       //do something  

  20.     }  

  21.     else{  

  22.       //do something  

  23.     }  

  24.     //do something  

  25. }  

  26.   

  27. int main(int argc, char **argv){  

  28.     int arr={2,4,3,656,23};  

  29.     my_sort(arr, 5, sizeof(int), my_compare);  

  30.     //do something  

  31.     return 0;  

  32. }  


         用typedef來定義的好處,,就是可以使用一個簡短的名稱來表示一種類型,,而不需要總是使用很長的代碼來,這樣不僅使得代碼更加簡潔易讀,,更是避免了代碼敲寫容易出錯的問題,。強(qiáng)烈推薦各位在定義結(jié)構(gòu)體,指針(尤其是函數(shù)指針)等比較復(fù)雜的結(jié)構(gòu)時,,使用typedef來定義,。


后序

         關(guān)于指針與數(shù)組的部分將在后續(xù)介紹,多級指針在數(shù)組中的應(yīng)用也會更加深入的介紹,。


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多