国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > C++ > 正文

徹底搞定 C/C++ 指針

2019-11-11 00:06:51
字體:
來源:轉載
供稿:網友
1.語言中變量的實質要理解C指針,我認為一定要理解C中“變量”的存儲實質, 所以我就從“變量”這個東西開始講起吧!先來理解理解內存空間吧!請看下圖:內存地址→  6      7   8      9   10      11      12       13-----------------------------------------------------------------。。。 |   |   |   |   |  |   |   |.。------------------------------- ----------------------------------如圖所示,內存只不過是一個存放數據的空間,就好像我 的看電影時的電影院中的座位一樣。每個座位都要編號,我們的內存要存放各種各樣的數據,當然我們 要知道我們的這些數據存放在什么位置吧!所以內存也要象座位一樣進行編號了,這就是我們所說的內 存編址。座位可以是按一個座位一個號碼的從一號開始編號,內存則是按一個字節一個字節進行編址, 如上圖所示。每個字節都有個編號,我們稱之為內存地址。好了,我說了這么多,現在你能理解內存空 間這個概念嗎?我們繼續看看以下的C、C++語言變量申明:int I;char a;每次我們要使用某變量時都要事先這樣申明它,它其實是內存中申請了一個名為i的整型變量寬 度的空間(DOS下的16位編程中其寬度為二個字節),和一個名為a的字符型變量寬度的空間(占一個字 節)。我們又如何來理解變量是如何存在的呢。當我們如下申明變量時:int I;char a;內存中的映象可能如下圖:內存地址→   6      7   8       9      10      11    12      13----------------------- -------------------------------------------。。。|   |   |   |   |   |   |   |.。------------------------------------------------------------------變量名|→i    ←|→a  ←|圖中可看出,i在內存起始地址為6上申請了 兩個字節的空間(我這里假設了int的寬度為16位,不同系統中int的寬度是可能不一樣的),并命名為 i. a在內存地址為8上申請了一字節的空間,并命名為a.這樣我們就有兩個不同類型的變量了。2.賦值給變量再看下面賦值:i=30a=‘t’你當然知 道個兩個語句是將30存入i變量的內存空間中,將‘t’字符存入a變量的內存空間中。我們可 以這樣的形象理解啦:內存地址→   6      7   8      9      10       11    12      13------------------------------------------------ -----------------------。。。 |   30      |  ‘t’  |   |   |    |   |.。-------------------------------------------------------------------- ---|→i    ←|→a  ←|3.變量在哪里?(即我想知道變量的地 址)好了,接下來我們來看看&i是什么意思?是取i變量所在的地址編號嘛!我們可 以這樣讀它:返回i變量的地址編號。你記住了嗎?我要在屏幕上顯示變量的地址值的話,可以 寫如下代碼:PRintf(“%d”,&i);以上圖的內存映象所例,屏幕上 顯示的不是i值30,而是顯示i的內存地址編號6了。當然實際你操作的時,i變量的地址值不會是這個數 了。這就是我認為作為初學者們所應想象的變量存儲實質了。請這樣理解吧!最后總結代碼如下:int main(){int i=39;printf(“%d/n”,i);    //①printf(“%d/n”, &i);  //②}現在你可知道 ①、②兩個printf分別在屏幕上輸出的是i的什么東西啊?好啦!下面我們就開始真正進入指針 的學習了。二、指針是什么東西想說弄懂你不容易啊!我們許多初學指針的人都要這樣的感慨。我常常在思索它,為什么呢?其實生活中處處都有指針。我們也處處在使用它。有了它我們的生活才更加方便 了。沒有指針,那生活才不方便。不信?你看下面的例子。這是一個生活中的例子:比如說你要 我借給你一本書,我到了你宿舍,但是你人不在宿舍,于是我把書放在你的2層3號的書架上,并寫了一 張紙條放在你的桌上。紙條上寫著:你要的書在第2層3號的書架上。當你回來時,看到這張紙條。你就 知道了我借與你的書放在哪了。你想想看,這張紙條的作用,紙條本身不是書,它上面也沒有放著書。 那么你又如何知道書的位置呢?因為紙條上寫著書的位置嘛!其實這張紙條就是一個指針了。它上面的 內容不是書本身,而是書的地址,你通過紙條這個指針找到了我借給你的本書。那么我們C,C++ 中的指針又是什么呢?請繼續跟我來吧,看下面看一個申明一整型指針變量的語句如下:int * pi;pi是一個指針,當然我們知道啦,但是這樣說,你就以為pi一定是個多么特別的東西了。其 實,它也只過是一個變量而已。與上一篇中說的變量并沒有實質的區別。不信你看下面圖。內存 地址→6     7   8      9     10     11      12     13      14--------------------------------------------------------------...|    30      |  ‘t’ |      |      |      |      |       |      |……--------------------------------------------------- -----------變量 |→i   ←|→a   ←|       |→ pi       ←|(說明:這里我假設了指針只占2個字節寬度,實際上在32位系統中,指針的寬度 是4個字節寬的,即32位。)由圖示中可以看出,我們使用int *Pi申明指針變量; 其實是在內存的某處 申明一個一定寬度的內存空間,并把它命名為Pi.你能在圖中看出pi與前面的i,a 變量有什么本質區別 嗎,沒有,當然沒有!pi也只不過是一個變量而已嘛!那么它又為什么會被稱為指針?關鍵是我們要讓 這個變量所存儲的內容是什么。現在我要讓pi成為真正有意義上的指針。請接著看下面語句:pi=&i;你應該知道 &i是什么意思吧!再次提醒你啦:這是返回i變量的地址編 號。整句的意思就是把i地址的編號賦值給pi,也就是你在pi上寫上i的地址編號。結果如下圖所示:內存地址→6     7   8   9   10     11    12     13     14------------------------------------------------------------------...|      30      |  ‘t’  |      |      |     6      |       |      |……----------------------------------------------- -------------------變量 |→i   ←|→a    ←|       |→ pi     ←|你看,執行完pi=&i;后,在圖示中的系統中,pi的值是6.這 個6就是i變量的地址編號,這樣pi就指向了變量i了。你看,pi與那張紙條有什么區別?pi不就是那張紙 條嘛!上面寫著i的地址,而i就是那個本書。你現在看懂了嗎?因此,我們就把pi稱為指針。所以你要 記住,指針變量所存的內容就是內存的地址編號!好了,現在我們就可以通過這個指針pi來訪問到i這個 變量了,不是嗎?。看下面語句:printf(“%d”,*pi);那么*pi什么意 思呢?你只要這樣讀它:pi內容所指的地址的內容(嘻嘻,看上去好像在繞口令了),就pi這張“ 紙條”上所寫的位置上的那本 “書”——i .你看,Pi內容是6,也就是說 pi指向內存編號為6的地址。*pi嘛!就是它所指地址的內容,即地址編號6上的內容了。當然就是30的值 了。所以這條語句會在屏幕上顯示30.也就是說printf(“%d”,*pi);語句等價于printf ( “%d”, i ) ,請結合上圖好好體會吧!各位還有什么疑問,可以發Email: yyf977@163.com.到此為止,你掌握了類似&i , *pi寫法的含義和相關操作嗎。總的一句話 ,我們的紙條就是我們的指針,同樣我們的pi也就是我們的紙條!剩下的就是我們如何應用這張紙條了 。最后我給你一道題:程序如下char  a,*paa=10pa=&a*pa=20printf( “%d”, a)你能直接看出輸出的結果是什么嗎?如 果你能,我想本篇的目的就達到了。好了,就說到這了。Happy to Study!在下篇中我將談談“指 針的指針”即對int * * ppa;中ppa 的理解。1.數組元素看下面代碼int i,a[]={3,4,5,6,7,3,7,4,4,6};for (i=0;i<=9;i++){printf ( “%d”, a[i] );}很顯然,它是顯示a 數組的各元素值。我們還可以這樣訪問元素,如下int i,a[]={3,4,5,6,7,3,7,4,4,6};for (i=0;i<=9;i++){printf ( “%d”,  *(a+i) );}它的結果和作用完全一樣2. 通過指針訪問數組元素int i,*pa,a[]={3,4,5,6,7,3,7,4,4,6};pa =a  ;//請注意數組名a直接賦值給指針 pafor (i=0;i<=9;i++){printf ( “%d”, pa[i] );} 很顯然,它也是顯示a 數組的各元素值。另外與數組名一樣也可如下:int i,*pa,a[]={3,4,5,6,7,3,7,4,4,6};pa =a;for (i=0;i<=9;i++){printf ( “%d”, *(pa+i) );}看pa=a即數組名賦值給指針,以及通過數組名、指針對元素的訪問形式看,它們并沒有什么區別,從 這里可以看出數組名其實也就是指針。難道它們沒有任何區別?有,請繼續。3. 數組名與指針變量的區別請看下面的代碼:int i,*pa,a[]={3,4,5,6,7,3,7,4,4,6};pa =a;for (i=0;i<=9;i++){printf ( “%d”, *pa );pa++ ;  //注意這里,指針值被修改} 可以看出,這段代碼也是將數組各元素值輸出。不過,你把{}中的pa改成a試試。你會發現程序編譯 出錯,不能成功。看來指針和數組名還是不同的。其實上面的指針是指針變量,而數組名只是一個指針 常量。這個代碼與上面的代碼不同的是,指針pa在整個循環中,其值是不斷遞增的,即指針值被修改了 。數組名是指針常量,其值是不能修改的,因此不能類似這樣操作:a++.前面4,5節中pa[i],*(pa+i )處,指針pa的值是使終沒有改變。所以變量指針pa與數組名a可以互換。4. 申明指針常量再請看下面的代碼:int i, a[]={3,4,5,6,7,3,7,4,4,6};int * const pa=a;//注意const的位置:不是 const int * pa,for (i=0;i<=9;i++){printf ( “%d”, *pa );pa++ ;  //注意這里,指針值被修改}這時候的代碼能成功編譯嗎?不能。因為pa指針被定義為常量指針了。這時與數組名a已經沒有不同 。這更說明了數組名就是常量指針。但是…int * const a={3,4,5,6,7,3,7,4,4,6};//不行int a[]={3,4,5,6,7,3,7,4,4,6};//可以,所以初始化數組時必定要這樣。以上都是在VC6.0上實驗。1 int i 說起你知道我們申明一個變量時象這樣int i ;這個i是可能在它處重新變賦值的。 如下:int i=0;//…i=20;//這里重新賦值了不過有一天我的程 序可能需要這樣一個變量(暫且稱它變量),在申明時就賦一個初始值。之后我的程序在其它任何處都 不會再去重新對它賦值。那我又應該怎么辦呢?用const .//**************const int ic =20;//…ic=40;//這樣是不可以的,編譯時是無法通過,因為我們不能對 const 修飾的ic重新賦值的。//這樣我們的程序就會更早更容易發現問題了。//**************有了const修飾的ic 我們不稱它為變量,而稱符號常量,代表著20這 個數。這就是const 的作用。ic是不能在它處重新賦新值了。認識了const 作用之后,另外,我 們還要知道格式的寫法。有兩種:const int ic=20;與int const ic=20;。它們是完全相同的。這一 點我們是要清楚。總之,你務必要記住const 與int哪個寫前都不影響語義。有了這個概念后,我們來看 這兩個家伙:const int * pi與int const * pi ,按你的邏輯看,它們的語義有不同嗎?呵呵,你只要 記住一點,int 與const 哪個放前哪個放后都是一樣的,就好比const int ic;與int const ic;一樣 。也就是說,它們是相同的。好了,我們現在已經搞定一個“雙包胎”的問題。那么 int * const pi與前兩個式子又有什么不同呢?我下面就來具體分析它們的格式與語義吧!2 const int * pi的語義我先來說說const int * pi是什么作用 (當然int const * pi也是一樣 的,前面我們說過,它們實際是一樣的)。看下面的例子://*************代碼開始 ***************int i1=30;int i2=40;const int * pi=&i1;pi=&i2;    //4.注意這里,pi可以在任意時候重新賦值一個新內存地址i2=80;    //5.想想看:這里能用*pi=80;來代替嗎?當然不能printf( “%d”, *pi ) ;  //6. 輸出是80//*************代碼結束***************語義分析:看出來了 沒有啊,pi的值是可以被修改的。即它可以重新指向另一個地址的,但是,不能通過*pi來修改i2的值。 這個規則符合我們前面所講的邏輯嗎?當然符合了!首先const  修飾的是整個*pi(注意,我 寫的是*pi而不是pi)。所以*pi是常量,是不能被賦值的(雖然pi所指的i2是變量,不是常量)。其次,pi前并沒有用const 修飾,所以pi是指針變量,能被賦值重新指向另一內存地址的。你可 能會疑問:那我又如何用const 來修飾pi呢?其實,你注意到int * const pi中const 的位置就大概可 以明白了。請記住,通過格式看語義。哈哈,你可能已經看出了規律吧?那下面的一節也就沒必要看下 去了。不過我還得繼續我的戰斗!3 再看int * const pi確實,int * const pi與前面 的int const * pi會很容易給混淆的。注意:前面一句的const 是寫在pi前和*號后的,而不是寫在*pi 前的。很顯然,它是修飾限定pi的。我先讓你看例子://*************代碼開始 ***************int i1=30;int i2=40;int * const pi=&i1;//pi=&i2;    4.注意這里,pi不能再這樣重新賦值了,即不能再指向另一個新地址。   //所以我已經注釋了它。i1=80;    //5.想想看:這里能用*pi=80;來代替嗎?可以,這 里可以通過*pi修改i1的值。     //請自行與前面一個例子比較。printf( “% d”, *pi ) ;  //6.輸出是80//***************代碼結束 *********************語義分析:看了這段代碼,你明白了什么?有沒有發現 pi值是不能重新賦值修改了。它只能永遠指向初始化時的內存地址了。相反,這次你可以通過*pi來修改 i1的值了。與前一個例子對照一下吧!看以下的兩點分析1)pi因為有了const 的修飾,所以只 是一個指針常量:也就是說pi值是不可修改的(即pi不可以重新指向i2這個變量了)(看第4行)。2)整個*pi的前面沒有const 的修飾。也就是說,*pi是變量而不是常量,所以我們可以通過 *pi來修改它所指內存i1的值(看5行的注釋)總之一句話,這次的pi是一個指向int變量類型數 據的指針常量。我最后總結兩句:1) 如果const 修飾在*pi前則不能改的是*pi(即不能 類似這樣:*pi=50;賦值)而不是指pi.2) 如果const 是直接寫在pi前則pi不能改(即不能類似 這樣:pi=&i;賦值)。請你務必先記住這兩點,相信你一定不會再被它們給搞糊了。現在 再看這兩個申明語句int const *pi和int * const pi時,呵呵,你會頭昏腦脹還是很輕松愜意?它們各 自申明的pi分別能修改什么,不能修改什么?再問問自己,把你的理解告訴我吧,可以發帖也可以發到 我的郵箱(我的郵箱yyf977@163.com)!我一定會答復的。3)  補充三種情況。這里, 我再補充以下三種情況。其實只要上面的語義搞清楚了,這三種情況也就已經被包含了。不過作為三種 具體的形式,我還是簡單提一下吧!情況一:int * pi指針指向const int i常量的情況//**********begin*****************const int i1=40;int *pi;pi=&i1; //這樣可以嗎?不行,VC下是編譯錯。     //const int 類型的i1的地址 是不能賦值給指向int 類型地址的指針pi的。否則pi豈不是能修改i1的值了嗎!pi=(int* ) &i1;  // 這樣可以嗎?強制類型轉換可是C所支持的。   //VC下編譯通過,但是仍 不能通過*pi=80來修改i1的值。去試試吧!看看具體的怎樣。//***********end***************情況二:const int * pi指針指向const int i1的 情況//*********begin****************const int i1=40;const int * pi;pi=&i1;//兩個類型相同,可以這樣賦值。很顯然,i1的值無論是通過pi還是i1都不能修 改的。//*********end*****************情況三:用const int * const pi申明 的指針//***********begin****************int iconst int * const pi=&i;//你能想象pi能夠作什么操作嗎?pi值不能改,也不能通過pi修改i的值。因為不管是*pi還 是pi都是const的。//************end****************下篇預告:函數參數的 指針傳遞,值傳遞,引用傳遞 迷惑(以為a,b已經代替了x,y,對x,y的操作就是對a,b的操作了,這 是一個錯誤的觀點啊!)。一、三道考題開講之前,我先請你做三道題目。(嘿嘿,得先把你的頭腦搞昏才行 ……唉呀,誰扔我雞蛋?)1.考題一:程序代碼如下:void Exchg1(int x, int y){int tmp;tmp=x;x=y;y=tmp;printf (“x=%d,y=%d/n”,x,y)}void main(){int a=4,b=6;Exchg1 (a,b) ;printf(“a=%d,b=%d/n”,a,b)}輸出的結果 :x=____, y=____a=____, b=____問下劃線的部分應是什么,請完成。2.考題二:代碼如下。Exchg2(int *px, int *py){int tmp=*px;*px=*py;*py=tmp;print(“*px=%d,*py=%d/n”,*px,*py);}main(){int a=4;int b=6;Exchg2( &a,&b);Print (“a=%d,b=%d/n”, a, b);}輸出的結果為:*px=____, *py=____a=____, b=____問下劃線的部分應是什么,請完成。3.考題三:Exchg2(int &x, int &y){int tmp=x;x=y;y=tmp;print(“x=%d,y=%d/n”,x,y);}main(){int a=4;int b=6;Exchg2(a,b);Print(“a=%d,b=%d/n”, a, b);}輸 出的結果:x=____, y=____a=____, b=____問下劃線的部分輸出的應是什么, 請完成。你不在機子上試,能作出來嗎?你對你寫出的答案有多大的把握?正確的答案 ,想知道嗎?(呵呵,讓我慢慢地告訴你吧!)好,廢話少說,繼續我們的探索之旅了。我們都知道:C語言中函數參數的傳遞有:值傳遞,地址傳遞,引用傳遞這三種形式。題一為值 傳遞,題二為地址傳遞,題三為引用傳遞。不過,正是這幾種參數傳遞的形式,曾把我給搞得暈頭轉向 。我相信也有很多人與我有同感吧?下面請讓我逐個地談談這三種傳遞形式。二、函數 參數傳遞方式之一:值傳遞1.值傳遞的一個錯誤認識先看題一中Exchg1函數的定義:void Exchg1(int x, int y)   //定義中的x,y變量被稱為Exchg1函數的形式參數{int tmp;tmp=x;x=y;y=tmp;printf(“x=%d,y=% d/n”,x,y)}問:你認為這個函數是在做什么呀?答:好像是對參數 x,y的值對調吧?請往下看,我想利用這個函數來完成對a,b兩個變量值的對調,程序如下:void main(){int a=4,b=6;Exchg1 (a,b)     //a,b變量為 Exchg1函數的實際參數。/  printf(“a=%d,b=%d/n”,a,b)}我問:Exchg1 ()里頭的  printf(“x=%d,y=%d/n”,x,y)語句會輸出什么啊?我再問:Exchg1 ()后的  printf(“a=%d,b=%d/n”,a,b)語句輸出的是什么 ?程序輸出的結果是:x=6 , y=4a=4 , b=6  //為什么不是a=6,b=4呢?奇怪,明明我把a,b分別代入了x,y中,并在函數里完成了兩個變量值的交換,為什么a,b變量 值還是沒有交換(仍然是a==4,b==6,而不是a==6,b==4)?如果你也會有這個疑問,那是因為你跟本 就不知實參a,b與形參x,y的關系了。2.一個預備的常識為了說明這個問題,我先給出 一個代碼:int a=4;int x;x=a;x=x+3;看好了沒,現在我問 你:最終a值是多少,x值是多少?(怎么搞的,給我這個小兒科的問題。還不簡單,不就是a==4   x==7嘛!)在這個代碼中,你要明白一個東西:雖然a值賦給了x,但是a變量并不是x變量哦 。我們對x任何的修改,都不會改變a變量。呵呵!雖然簡單,并且一看就理所當然,不過可是一個很重 要的認識喔。3.理解值傳遞的形式看調用Exch1函數的代碼:main(){int a=4,b=6;Exchg1(a,b) //這里調用了Exchg1函數  printf(“a=% d,b=%d”,a,b)}Exchg1(a,b)時所完成的操作代碼如下所示。int x=a;//←int y=b;//←注意這里,頭兩行是調用函數時的隱含操作int tmp;tmp=x;x=y;y=tmp;請注意在調用執行Exchg1函數的操作中我人為地加上 了頭兩句:int x=a;int y=b;這是調用函數時的兩個隱含動作。它確實存在, 現在我只不過把它顯式地寫了出來而已。問題一下就清晰起來啦。(看到這里,現在你認為函數里面交 換操作的是a,b變量或者只是x,y變量呢?)原來 ,其實函數在調用時是隱含地把實參a,b 的 值分別賦值給了x,y,之后在你寫的Exchg1函數體內再也沒有對a,b進行任何的操作了。交換的只是x, y變量。并不是a,b.當然a,b的值沒有改變啦!函數只是把a,b的值通過賦值傳遞給了x,y,函數里頭 操作的只是x,y的值并不是a,b的值。這就是所謂的參數的值傳遞了。哈哈,終于明白了,正是 因為它隱含了那兩個的賦值操作,才讓我們產生了前述的迷惑(以為a,b已經代替了x,y,對x,y的操 作就是對a,b的操作了,這是一個錯誤的觀點啊!)。指向另一指針的指針一、針概念:早在本系列第二篇中我就對指針的實質進行了闡述 。今天我們又要學習一個叫做指向另一指針地址的指針。讓我們先回顧一下指針的概念吧!當我 們程序如下申明變量:short int i;char a;short int * pi;程序會 在內存某地址空間上為各變量開辟空間,如下圖所示。內存地址→6     7  8      9     10     11    12    13     14    15------------------- ------------------------------------------------------------------…  |      |  |  |  |  |  |  |  |  |--------------------------------------- ----------------------------------------------|short int i |char a|  |short int * pi|圖中所示中可看出:i 變量在內存地址5的位置,占兩個字節。a變量在內存 地址7的位置,占一個字節。pi變量在內存地址9的位置,占兩個字節。(注:pi 是指針,我這 里指針的寬度只有兩個字節,32位系統是四個字節)接下來如下賦值:i=50;pi=&i;經過上在兩句的賦值,變量的內存映象如下:內存地址→6      7  8     9     10     11    12    13  14     15----- ---------------------------------------------------------------------------------…  |    50  |  |  |    6   |  |  |  |----------- ---------------------------------------------------------------------------|short int i |char a|  |short int * pi|看到沒有:短整型指針變量pi的值為6,它就是I變量的內 存起始地址。所以,這時當我們對*pi進行讀寫操作時,其實就是對i變量的讀寫操作。如:*pi=5;   //就是等價于I=5;你可以回看本系列的第二篇,那里有更加詳細的解說。二、指針的地址與指向另一指針地址的指針在上一節中,我們看到,指針變量本身與其 它變量一樣也是在某個內存地址中的,如pi的內存起始地址是10.同樣的,我們也可能讓某個指針指向這 個地址。看下面代碼:short int * * ppi;    //這是一個指向指針的指針,注意 有兩個*號ppi=π第一句:short int * * ppi;——申明了一個指針變量 ppi,這個ppi是用來存儲(或稱指向)一個short int * 類型指針變量的地址。第二句: &pi那就是取pi的地址,ppi=π就是把pi的地址賦給了ppi.即將地址值10賦值給ppi.如下圖:內存地址→6     7  8     9     10     11    12    13   14    15------------------------------------------------------------------------ ------------…  |    50     |  |  |  6  |  10  |   |---------------------------------------------------------------------------------- --|short int i|char a|  |short int * pi|short int ** ppi|從圖中看出,指針變 量ppi的內容就是指針變量pi的起始地址。于是……ppi的值是多少呢? ——10.*ppi的值是多少呢?——6,即pi的值。**ppi的值是多少 呢?——50,即I的值,也是*pi的值。呵呵!不用我說太多了,我相信你應明白這種 指針了吧!三、一個應用實例1. 設計一個函數:void find1(char array[], char search, char * pi)要求:這個函數參數中的數組array是以0值為結束的字符串,要求在字符 串array中查找字符是參數search里的字符。如果找到,函數通過第三個參數(pa)返回值為array字符 串中第一個找到的字符的地址。如果沒找到,則為pa為0.設計:依題意,實現代碼如下void find1(char [] array, char search, char * pa){int i;for (i=0;*(array+i)!=0;i++){if (*(array+i)==search){pa=array+ibreak;}else if (*(array+i)==0){pa=0;break;}}}你覺得這個函數能實現所要求的功能嗎?調試:我下面調用這個函數 試試。void main(){char str[]={“afsdfsdfdf/0”};  //待 查找的字符串char a=’d’;   //設置要查找的字符char * p=0;  //如果 查找到后指針p將指向字符串中查找到的第一個字符的地址。find1(str,a,p);  //調用函數以實 現所要操作。if (0==p ){printf (“沒找到!/n”);//1.如果沒找到則 輸出此句}else{printf(“找到了,p=%d”,p);  //如果找到則 輸出此句}}分析:上面代碼,你認為會是輸出什么呢?運 行試試。唉!怎么輸出的是:沒有找到!而不是:找到了,……。明明a值為‘d’,而str字符串的第四個字符是‘d’,應該找得到呀!再 看函數定義處:void find1(char [] array, char search, char * pa)看調用處:find1( str,a,p);依我在第五篇的分析方法,函數調用時會對每一個參數進行一個隱含的賦值操作 。整個調用如下:array=str;search=a;pa=p;    //請注意:以 上三句是調用時隱含的動作。int i;for (i=0;*(array+i)!=0;i++){if (* (array+i)==search){pa=array+ibreak;}else if (*(array+i)==0) {pa=0;break;}}哦!參數pa與參數search的傳遞并沒 有什么不同,都是值傳遞嘛(小語:地址傳遞其實就是地址值傳遞嘛)!所以對形參變量pa值(當然值 是一個地址值)的修改并不會改變實參變量p值,因此p的值并沒有改變(即p的指向并沒有被改變)。(如果還有疑問,再看一看《第五篇:函數參數的傳遞》了。)修正:void find2(char [] array, char search, char ** ppa){int i;for (i=0;*(array+i)!=0;i++){if (*(array+i)==search){*ppa=array+ibreak;}else if (*(array+i)==0){*ppa=0;break;}}}主函數的調用處改如下:find2(str,a, &p);  //調用函數以實現所要操作。再分析:這樣調用函數時的整個操作變成如 下:array=str;search=a;ppa=&p;    //請注意:以上三句是調用 時隱含的動作。int i;for (i=0;*(array+i)!=0;i++){if (*(array+i) ==search){*ppa=array+ibreak;}else if (*(array+i)==0){*ppa=0;break;}}看明白了嗎?ppa指向指針p的地址 。對*ppa的修改就是對p值的修改。你自行去調試。經過修改后的程序就可以完 成所要的功能了。看懂了這個例子,也就達到了本篇所要求的目的。函數名與函數指針一 數調用一個通常的函數調用的例子://自行包含 頭文件void MyFun(int x);    //此處的申明也可寫成:void MyFun( int );int main(int argc, char* argv[]){MyFun(10);     //這里是調用MyFun(10);函數return 0;}void MyFun(int x)  //這里定義一個MyFun函數{printf (“%dn”,x);}這個MyFun函數是一個無返回值的函數,它并不完成什 么事情。這種調用函數的格式你應該是很熟悉的吧!看主函數中調用MyFun函數的書寫格式:MyFun(10);我們一開始只是從功能上或者說從數學意義上理解MyFun這個函數,知道 MyFun函數名代表的是一個功能(或是說一段代碼)。直到——學習到函數指 針概念時。我才不得不在思考:函數名到底又是什么東西呢?(不要以為這是沒有什么意義的事 噢!呵呵,繼續往下看你就知道了。)二 函數指針變量的申明就象某一數據變量的內存 地址可以存儲在相應的指針變量中一樣,函數的首地址也以存儲在某個函數指針變量里的。這樣,我就 可以通過這個函數指針變量來調用所指向的函數了。在C系列語言中,任何一個變量,總是要先 申明,之后才能使用的。那么,函數指針變量也應該要先申明吧?那又是如何來申明呢?以上面的例子 為例,我來申明一個可以指向MyFun函數的函數指針變量FunP.下面就是申明FunP變量的方法:void (*FunP)(int) ;   //也可寫成void (*FunP)(int x);你看,整個函 數指針變量的申明格式如同函數MyFun的申明處一樣,只不過——我們把MyFun改成(*FunP) 而已,這樣就有了一個能指向MyFun函數的指針FunP了。(當然,這個FunP指針變量也可以指向所有其它 具有相同參數及返回值的函數了。)三 通過函數指針變量調用函數有了FunP指針變量后 ,我們就可以對它賦值指向MyFun,然后通過FunP來調用MyFun函數了。看我如何通過FunP指針變量來調 用MyFun函數的://自行包含頭文件void MyFun(int x);    //這個申明也可寫 成:void MyFun( int );void (*FunP)(int );   //也可申明成void(*FunP)(int x),但習慣 上一般不這樣。int main(int argc, char* argv[]){MyFun(10);     //這是 直接調用MyFun函數FunP=&MyFun;  //將MyFun函數的地址賦給FunP變量(*FunP)(20);     //這是通過函數指針變量FunP來調用MyFun函數的。}void MyFun(int x)  //這里 定義一個MyFun函數{printf(“%dn”,x);}請看黑體字部 分的代碼及注釋。運行看看。嗯,不錯,程序運行得很好。哦,我的感覺是:MyFun與 FunP的類型關系類似于int 與int *的關系。函數MyFun好像是一個如int的變量(或常量),而FunP則像 一個如int *一樣的指針變量。int i,*pi;pi=&i;    //與FunP=&MyFun 比較。(你的感覺呢?)呵呵,其實不然——四 調用函數的其它書 寫格式函數指針也可如下使用,來完成同樣的事情://自行包含頭文件void MyFun(int x);void (*FunP)(int );    //申明一個用以指向同樣參數,返回值函數 的指針變量。int main(int argc, char* argv[]){MyFun(10);     //這里是 調用MyFun(10);函數FunP=MyFun;  //將MyFun函數的地址賦給FunP變量FunP(20);    //這是通過函數指針變量來調用MyFun函數的。return 0;}void MyFun(int x)  // 這里定義一個MyFun函數{printf(“%dn”,x);}我改了黑 體字部分(請自行與之前的代碼比較一下)。運行試試,啊!一樣地成功。咦?FunP=MyFun;可以這樣將MyFun值同賦值給FunP,難道MyFun與FunP是同一數據類型(即 如同的int 與int的關系),而不是如同int 與int*的關系了?(有沒有一點點的糊涂了?)看 來與之前的代碼有點矛盾了,是吧!所以我說嘛!請容許我暫不給你解釋,繼續看以下幾種情況 (這些可都是可以正確運行的代碼喲!):代碼之三:int main(int argc, char* argv[]){MyFun(10);     //這里是調用MyFun(10);函數FunP=&MyFun;  //將MyFun函數的地址賦給FunP變量FunP(20);    //這是通過函數指 針變量來調用MyFun函數的。return 0;}代碼之四: int main(int argc, char* argv[]){MyFun(10);     //這里是調用MyFun(10);函數FunP=MyFun;  //將MyFun函數的地址賦給FunP變量(*FunP)(20);    //這是通過函數指針 變量來調用MyFun函數的。return 0;}真的是可以這樣的噢!(哇 !真是要暈倒了!)還有吶!看——int main(int argc, char* argv[]){(*MyFun)(10);     //看,函數名MyFun也可以有這樣的調用格式return 0;}你也許第一次見到吧:函數名調用也可以是這樣寫的啊!(只不過 我們平常沒有這樣書寫罷了。)那么,這些又說明了什么呢?呵呵!依據以往的知識和 經驗來推理本篇的“新發現”,我想就連“福爾摩斯”也必定會由此分析并推斷 出以下的結論:1. 其實,MyFun的函數名與FunP函數指針都是一樣的,即都是函數指針。MyFun 函數名是一個函數指針常量,而FunP是一個函數數指針變量,這是它們的關系。2. 但函數名調 用如果都得如(*MyFun)(10);這樣,那書寫與讀起來都是不方便和不習慣的。所以C語言的設計者們 才會設計成又可允許MyFun(10);這種形式地調用(這樣方便多了并與數學中的函數形式一樣,不是嗎 ?)。3. 為統一起見,FunP函數指針變量也可以FunP(10)的形式來調用。4. 賦值時 ,即可FunP=&MyFun形式,也可FunP=MyFun.上述代碼的寫法,隨便你愛怎么著!請 這樣理解吧!這可是有助于你對函數指針的應用嘍!最后——補充說明一點 :在函數的申明處:void MyFun(int );    //不能寫成void (*MyFun)(int )。void (*FunP)(int );   //不能寫成void FunP(int )。(請看注釋)這一點 是要注意的。五 定義某一函數的指針類型:就像自定義數據類型一樣,我們也可以先定 義一個函數指針類型,然后再用這個類型來申明函數指針變量。我先給你一個自定義數據類型的 例子。typedef int* PINT;    //為int* 類型定義了一個PINT的別名 int main(){int x;PINT px=&x;   //與int * px=&x;是等價的。PINT類型其 實就是int * 類型*px=10;  //px就是int*類型的變量return 0;}根據注釋,應該不難看懂吧!(雖然你可能很少這樣定義使用,但以后學習Win32編程時會經常見到的。 )下面我們來看一下函數指針類型的定義及使用:(請與上對照!)//自行包含 頭文件void MyFun(int x);    //此處的申明也可寫成:void MyFun( int );typedef void (*FunType)(int );   //這樣只是定義一個函數指針類型FunType FunP;    //然后 用FunType類型來申明全局FunP變量int main(int argc, char* argv[]){//FunType FunP;    //函數指針變量當然也是可以是局部的 ,那就請在這里申明了。MyFun(10);FunP=&MyFun;(*FunP)(20);return 0;}void MyFun(int x){printf(“%dn”,x);}看黑體部分:首先,在void (*FunType)(int ); 前加了一個typedef .這樣只是定義一個名為FunType函數指針類型,而不是一 個FunType變量。然后,FunType FunP;  這句就如PINT px;一樣地申明一個FunP變量。其它相同。整個程序完成了相同的事。這樣做法的好處是:有了FunType類型后 ,我們就可以同樣地、很方便地用FunType類型來申明多個同類型的函數指針變量了。如下:FunType FunP2;FunType FunP3;//……六 函數指針作為某個函數的參數既然函數指針變量是一個變量,當然也可以作為某個函數的參數來使用的。所以 ,你還應知道函數指針是如何作為某個函數的參數來傳遞使用的。給你一個實例:要求 :我要設計一個CallMyFun函數,這個函數可以通過參數中的函數指針值不同來分別調用MyFun1、MyFun2 、MyFun3這三個函數(注:這三個函數的定義格式應相同)。實現:代碼如下://自行包含頭文件void MyFun1(int x);void MyFun2(int x);void MyFun3(int x);typedef void (*FunType)(int ); //②. 定義一個函數指針類型FunType,與①函 數類型一至void CallMyFun(FunType fp,int x);int main(int argc, char* argv[]){CallMyFun(MyFun1,10);   //⑤. 通過CallMyFun函數分別調用三個不同的函數CallMyFun(MyFun2,20);CallMyFun(MyFun3,30);}void CallMyFun(FunType fp,int x) //③. 參數fp的類型是FunType。{fp(x);//④. 通過fp的指針執行傳遞進來的 函數,注意fp所指的函數是有一個參數的}void MyFun1(int x) // ①. 這是個有一個參數 的函數,以下兩個函數也相同{printf(“函數MyFun1中輸出:%dn”,x);}void MyFun2(int x){printf(“函數MyFun2中輸出:%dn”,x);}void MyFun3(int x){printf(“函數MyFun3中輸出:%dn”,x);}輸出結果:略分析:(看我寫的注釋。你可按我注釋的①②③④⑤順序自行 分析。------------------------------------------------------------------------------------------------------------文章出處:http://www.cnblogs.com/qiaogaojian/p/5861554.html
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

主站蜘蛛池模板: 临猗县| 汝城县| 桐乡市| 玉门市| 灌云县| 东莞市| 鄂州市| 盈江县| 阳泉市| 芒康县| 山西省| 浦县| 仁化县| 驻马店市| 新邵县| 定州市| 临澧县| 临邑县| 临沧市| 武宁县| 岐山县| 华安县| 长宁区| 宁远县| 洪湖市| 郓城县| 五指山市| 榕江县| 临澧县| 维西| 阜南县| 福清市| 嫩江县| 紫云| 庆阳市| 临江市| 广元市| 白玉县| 睢宁县| 元江| 青海省|