進階指標-指標的使用@ Bryan的C語言筆記 - 隨意窩

文章推薦指數: 80 %
投票人數:10人

我們在上一篇提到了指標運算子. 其中int *ptr 與*ptr的意義是不同的. int *ptr 是對於指標變數的定義,稱之為指向整數空間的指標. 而*ptr 我們稱之為反參照運算子, ... Bryan的C語言筆記程式語言與生活語言一樣多碰就會少碰會忘可當工具,可當興趣日誌相簿影音好友名片 201506230222進階指標-指標的使用?C這次我們要提到較深入的指標觀念了 如果要重新溫習一下簡易的指標概念 傳送門:指標入門-淺談指標的藝術 就讓我們來繼續了解這個雖然困難但非常好用的指標運算吧       我們在上一篇提到了指標運算子 其中int*ptr與*ptr的意義是不同的 int*ptr是對於指標變數的定義,稱之為指向整數空間的指標 而*ptr我們稱之為反參照運算子,表示傳ptr中位址的內容值 而接著我們要再介紹一個運算子 稱為取址運算元(addressoperator),表示法為"&",使用取址運算元可以回傳該空間的位址,例如: inta=5;//宣告一個變數a,初始值為5 int*value=&a;宣告一個可存放位址的變數value,並將變數a的位址值給value intb=*value;//將a位址的內容值傳給b print("b=%d",b);//這樣print出b的值,即為a的內容值,b=5 由上面的例子我們可以知道,&a只是將變數a空間的位址取出來,我們並不知道a的內容值,但我們知道a的記憶體位址資訊 而最後一行的b,由於value已經知道a的記憶體位址了,因此使用反參照運算子就可以將a的內容值傳出來給b,這樣b的結果就是5了    接下來就要來討論 char*value; int*value; double*value; 這幾個的差異了 因此我們先從"型態的記憶體大小"提起 我們都知道每個型態的記憶體空間都有特定的大小   型態 記憶體大小(位元/位元組) char 8/1 int 32/4 unsignedint 32/4 short 16/2 float 32/4 double 64/8 當你不知道的時候,求證的方法只有一種:使用sizeof()函式       sizeof()並不是只有使用在陣列上,他一樣可以使用在型態上 例如 sizeof(int)、sizeof(char)、sizeof(double)...etc 而這樣顯示出來的數值,都是以“位元組”來表示 也就是說你執行printf("%d",sizeof(int));這段程式碼的話 你得到的值就是4,因為一個int型態的空間即為4bytes 而一個位元組,就是8bit,這樣就可以換算出int型態為4*8=32bits    你可能會問說:請問一下可不可以這樣做 printf("%d",sizeof(int*)); printf("%d",sizeof(char*)); printf("%d",sizeof(double*)); . . . 正確答案是:YES! 你可以去實驗看看 執行上述程式碼的結果 你會得到一個驚人的結果 你會發現這三條程式碼做出來的數值都是“4” 這又是為什麼呢? 因為,int*是一個存放位址的型態宣告,因此以現今電腦的定址大小 皆為32bits定址,也就是每一個記憶體空間,都是用一個32bits的數值來表示 只是每個空間的大小,就要看你前面的型態而定 char就是一個1byte的空間 double就是一個8byte的空間 . . (以此類推)    接下來,我們討論一個觀念 我們稱之為call-by-value(傳值呼叫)與call-by-reference(傳址呼叫) 這種概念常用於函式的呼叫 我們來看下面的簡單的範例: //call-by-value intmain() { inta=5; intb=3; intc; c=intADD(a,b); printf("a+b=%d",c); return0; } intintADD(intx,inty) { intz; z=x+y; returnz; } 很明顯的我們可以知道結果為a+b=8 因為我們知道我們將值傳到函式內做運算,並且直接將值return回變數上,這樣就可以直接計算出我們的結果 那如果程式碼修改成下面這種情形呢? //call-by-reference(error) intmain() { inta=5; intb=3; intc; intADD(a,b,c); printf("a+b=%d",c); return0; } voidintADD(intx,inty,intz) { z=x+y; } 這樣的結果會與上面相同嗎? 結果是不會的,函式通常宣告結束後,後方所接的變數都是以“傳入”為導向 這樣對於前面的call-by-value的方式是行不通的,也就是說,雖然z改變了它本身的值 但是c卻一丁點都沒有被影響 所以如果真的要這樣寫 我們會修改成 //call-by-reference(correct) intmain() { inta=5; intb=3; intc; intADD(&a,&b,&c);//傳入a,b,c的位址 printf("a+b=%d",c); return0; } voidintADD(int*x,int*y,int*z) { *z=*x+*y;//改變了位址的內容值,相對的傳回去的內容值也會改變 } 這樣的話我們會將c的位址傳入,並且改變z的內容值,最後傳回去時c就會相對改變了 而我們把上述的兩個方法用一句白話文來做整理 第二種方法,就是利用call-by-reference來模擬出call-by-value的效果 由於C語言中並沒有直接定義call-by-reference的用法(C++中就有定義) 因此我們只能夠模擬出相同的效果而已    說了這麼多,想必也對call-by-value/call-by-reference有一些概念了 我們最後就來說說陣列與指標的使用方式吧    陣列的觀念,如果有疑惑的可以在下方留言或是在網路上尋找   參考網址:陣列 陣列經常使用在繪圖處理、查表、字串的運算上 其中以字串的運算最為常見 我們就用簡單的程式碼跟大家討論指標如何運用於字串處理   #include intmain() { charstring[]="WelcomeBryan'sCLanguageNote"; char*buf; buf=string; printf("%c",buf[8]); return0; }   由上面的程式碼,你可以看出最後的答案是多少嗎? 答案是:字元B 要懂上面的程式碼就要從陣列的宣告開始說起了 陣列宣告 通常有兩種方式 一種為definesize,另一種為unsize definesize就是: charstring[100]="WelcomeBryan'sCLanguageNote"; unsize的宣告就是: charstring[]="WelcomeBryan'sCLanguageNote"; 這兩個宣告的最大差別就是有沒有定義這麼陣列的大小 如果沒有定義必須對其初始化,其實也相對的定義出陣列的大小 而我們知道陣列的宣告必須要有 陣列的型態  陣列名稱[陣列大小]; 陣列的型態:就是int,char,short...etc 陣列的名稱:我們同時也稱為“此陣列的起始位址”,也就是陣列名稱代表的就是這個陣列的起始位址 依陣列的型態而定,如果是charbuf[100];,buf表示了此陣列的起始位址,其型態為constchar* 如果是intvalue[20];,value表示了此陣列的起始位址,其型態為constint*.......以此類推 所以說compiler看到一個陣列被宣告時,會賦予一個起始位址給陣列,而這個起始位址的名字,就是陣列的名稱 陣列名稱的位址不可以被改變 例如   charbuf[17];//buf的型態為constchar* char*addr=buf;//將buf的位址給addr,此敘述合理 addr=addr+1;//將addr位址+1 buf=addr;//不可被更動,此寫法是錯誤的   以上的程式碼是不可以被允許的 因為buf是一個常數,不是變數 這樣我們就可以將上面的程式碼解釋個很徹底了   #include intmain() { charstring[]="WelcomeBryan'sCLanguageNote";//宣告一個陣列,名稱為string char*buf;//宣告一個可存放位址的變數buf buf=string;//將陣列的起始位址傳給buf printf("%c",buf[8]);//取出buf中第8個index的內容,也就是string[8]的內容 return0; }   我們將buf=string,讓buf存放string陣列的起始位址 之後將起始位址後+8的位址的內容值(有點繞口令)print出來,其實就是取出string陣列中第8個index的內容而已     那你可能會問說 這個不需要反參照運算子嗎? 其實“[]”這個運算子我們稱為indexto,他跟反參照運算子的關係就是: a[i]=*(a+i) a[i]表示了陣列a第i個元素的內容值 *(a+i)代表了從陣列a的起始位址,計算第i個位址後,將a+i位址內容值傳出來 所以意義是相同的 所以上面的程式,其實也可以這麼寫   #include intmain() { charstring[]="WelcomeBryan'sCLanguageNote";//宣告一個陣列,名稱為string char*buf;//宣告一個可存放位址的變數buf buf=string+8;//將陣列的起始位址+8傳給buf printf("%c",*buf);//取出buf中內容,也就是string[8]的內容 return0; } 或是這樣寫(下面的程式碼的完整度較高)   #include intmain() { charstring[]="WelcomeBryan'sCLanguageNote";//宣告一個陣列,名稱為string char*buf;//宣告一個可存放位址的變數buf buf=string+(8*sizeof(char));//將陣列的起始位址+8傳給buf printf("%c",*buf);//取出buf中內容,也就是string[8]的內容 return0; }   為何說上面這段程式碼的完整度較高呢? 因為我們可以了解到第8個index的位址 就是從起始位址開始算8個char的記憶體位址 剛剛好sizeof(char)=1 因此string+8=string+sizeof(char)*8 答案是相同的 但是如果型態改成int,long,float,double結果就是錯誤的了(就必須加上sizeof(陣列型態))   由上面的範例與敘述 大概可以了解更深層的指標運用 之後提到指標的筆記,大部份都是針對指標的運用 而非指標的觀念 大約會提到 二維陣列與一維陣列的轉換char**->char* 字串複製、字串判斷、strstr的運用 malloc對於記憶體空間的產生與使用方式 希望上述內容可以幫助到大家~  [email protected]/Xuite日誌/回應(0)/引用(0)沒有上一則|日誌首頁|沒有下一則回應 加我為好友一個勇於追求新知識的中山人 熱愛攝影 勇於嘗試 理論與實踐並重:)日誌相簿影音 全部展開|全部收合 關鍵字 HiNet部落格背景音樂功能下架 b993011029311's新文章Homebrew-macOS不能沒有的超方便套件管理工具有關變數可視範圍(scope)的討論-1降級LinuxKernelVersiononUbuntuC程式型態轉換inttodouble&doubletoint指標應用:利用一維陣列模擬二維陣列好用的下載影片利器-youtube-dl進階指標-指標的使用指標入門-淺談指標的藝術C程式學習相關資料AboutThisNote b993011029311's新回應沒有新回應! Bryan的C語言筆記



請為這篇文章評分?