--- title: cpp pointer tags: c++ coding --- {%hackmd nzwXYrKQTGqIftPO5fCdKA %} # 指標 Pointer #### 指標是用來儲存記憶體位址的<span class="pred">變數</span>,可以透過指標來管理記憶體:包含<span class="program">配置、存值、取值與釋放</span>。 ## 12-1 指標變數 Pointer variable ### 1.1 宣告 - *type * name;* - *type * name = address;* - *type * nameA,  * nameB;* ### 1.2 指派 - *name = _address_;* 存取指標。 將 = 右側的記憶體位址指派給 = 左側的指標 ,指派後指標便可以透過這個記憶體位址,管理該記憶體空間。 - **name = value;* 存取指標管理的記憶體。 將 = 右側的值指派給 = 左側的指標所管理的記憶體空間。 ### 1.3 取值 - *name* 存取指標。 取得存放在指標中的的記憶體位址。 - **name* 存取指標管理的記憶體。 取得指標所管理的記憶體空間中的值。 <span class="pblue">範例程式</span> ```cpp= int main() { int value; value = 9; cout<<value<<endl; int * ptr; ptr = &value; cout<<ptr<<endl; *ptr = 100; cout<<*ptr<<endl; cout<<value<<endl; return 0; } ``` :::success OUTPUT: 9 ➜value =9 0x7ffd908f9afc ➜value address 100 ➜利用ptr管理value裡面的值 100 ➜實際上的value也會跟著改變 ::: ### 1.4 _address_ - & 取得一個已存在的變數的記憶體位址。 - new 配置指定型別的記憶體空間,並回傳該空間的記憶體位址。 - 另一個指標 將己存在的指標所儲存的記憶體位址,指派給另一個指標。 <span class="pblue">範例程式</span> ```cpp= int main() { int a; //"&" int * p =&a; *p = 49; cout<<"a="<<a<<endl; cout<<"p="<<*p<<endl; //"new" int * r; r = new int; *r = 200; cout<<"r="<<r<<endl; cout<<"*r="<<*r<<endl; //"another ptr" int * s = r; *s = 299; cout<<"*r="<<*r<<endl; cout<<"*s="<<*s<<endl; return 0; } ``` :::success OUTPUT: a=49 p=49 r=0x5633e28a62c0 *r=200 *r=299 *s=299 ::: ### 1.5 delete + 釋放記憶體位址。<span class="pblue">(失去了對該記憶體的掌控權) </span> 用 new 配置給指標的記憶體可以用delete釋放。 <span class="pblue">範例程式</span> ```cpp=28 //cont'd //"delete" delete r; cout<<"r="<<r<<endl; cout<<"*r="<<*r<<endl<<endl; cout<<"s="<<s<<endl; cout<<"*s="<<*s<<endl<<endl; return 0; ``` :::success OUTPUT: r=0x55dcd4f392c0 *r=1573736249 s=0x55dcd4f392c0 *s=1573736249 ::: ### 1.6 const(現在大概用不到XD) - const type * name; - type * const name; <br> ### 1.7 傳遞指標進入函數 <span class="pblue">範例程式</span> <span style="font-size:20px;color:#fa7a92"> 傳入值為指標的函數 </span> ```cpp= void add10(int * ptr1){ *ptr1+=10; //<=注意是數值加十,不是指標!!! return; } int main() { int a =111; cout<<"before add10 a="<<a<<endl; add10(&a); cout<<"after add10 a="<<a<<endl; return 0; } ``` :::success OUTPUT: before add10 a=111 after add10 a=121 ::: <br> <span class="pblue">範例程式</span> 交換兩數<span class="pred" style="font-size:20px">"錯誤程式"</span>(9.21)、 ```cpp= void swap(int x,int y){ int tmp=x; x=y; y=tmp; return; } int main() { int a=3, b=44; cout<<"before swap a= "<<a<<",b= "<<b<<endl; swap(a,b); cout<<"after swap a= "<<a<<",b= "<<b<<endl; return 0; } ``` :::success OUTPUT: before swap a= 3, b= 44 after swap a= 3, b= 44 ::: + <span style="color:#e89558;font-size:20px;">為什麼沒有交換呢????</span> ![](https://hackmd.io/_uploads/HJapGOz63.png) <br> 範例三<span style="font-size:20px;color:#fa7a92">"正確程式"</span>(9.21)、 ```cpp= void swap(int * ptr1,int * ptr2){ int tmp=*ptr1; *ptr1=*ptr2; *ptr2=tmp; return; } int main() { int a=5, b=20; cout<<"before swap a= "<<a<<",b= "<<b<<endl; swap(&a,&b); cout<<"after swap a= "<<a<<",b= "<<b<<endl; return 0; } ``` :::success OUTPUT: before swap a= 5, b= 20 after swap a= 20, b= 5 ::: <br> + <span style="color:#e89558;font-size:20px;">正確步驟的概念</span><br> ![](https://hackmd.io/_uploads/HyMh9K76n.png) <span class="pblue">範例程式</span><span style="font-size:20px;color:#fa7a92"> 傳回值為指標的函數 </span>(9.25)、 ```cpp= int * max(int * ptr1, int * ptr2){ //回傳值為int的指標型態,找出兩指標的最大值 if(*ptr1>*ptr2) return ptr1; else return ptr2; } int main() { int a =66, b=44, * pointer; pointer=max(&a,&b); cout<<"max is "<<*pointer<<endl; } ``` :::success OUTPUT: max is 66 ::: <br> ## 12-2指標與函數 Pointer and function ### 2.1 函數指標 ![](https://hackmd.io/_uploads/HJQVY5mTn.png) <span class="pblue">範例程式</span> ```cpp= int square(int a){ return (a*a); } int main() { int (*ptrf)(int); //HERE!!! ptrf=square; cout<<"square(5)= "<<(*ptrf)(5)<<endl; /*測試*/ cout<<"(*ptrf)(5)= "<<(*ptrf)(5)<<endl; cout<<"(ptrf)(5)= "<<(ptrf)(5)<<endl; return 0; } ``` :::success OUTPUT: square(5)= 25 (\*ptrf)(5)= 25 (ptrf)(5)= 25 ::: <br> + <span style="color:#8ce8ff">因為函數本身可以看做是指標,所以也可以當作引數傳遞進入另一個函數中</span> + 假設現在有一triangle()函數,引數為三角形底和高 ### 2.2 傳遞函數指標到其他函數中 ![](https://hackmd.io/_uploads/S1zFcjNTh.png) <span class="pblue">範例程式</span> ```cpp= /*9.3.4傳遞函數到其他函數中*/ //函數原型 double triangle(double, double), rectangle(double, double); void showarea(double,double,double (*pf)(double,double)); int main(){ cout<<"triangle(6,3.2)="; showarea(6,3.2,triangle); cout<<"rectangle(4,6.1)="; showarea(4,6.1,rectangle); } double triangle(double base, double height){ //三角形面積 return (base*height/2); } double rectangle(double base, double height){ //矩形面積 return (base*height); } void showarea(double x, double y, double (*pf)(double, double)){ //依照傳入的函數,去執行不同的函數 cout<<(*pf)(x,y)<<endl; return; } ``` :::success OUTPUT: triangle(6,3.2)=9.6 rectangle(4,6.1)=24.4 ::: <br> ## 12-3 指標與陣列 Pointer and array ### 3.1指標的算術運算 + 如果指標指向一個int, 則ptr+1會加上4byte + 可以很好用在存取陣列的元素(連續特性) + "陣列名稱奔身世一個存放位址的<span class="pred">指標常數</span>,他剛好指向陣列的位址" + 指標常數非指標變數,我們不行改變陣列指向的位址(他就只能指向陣列) ```cpp= /*9.4.1指標常數的值與位址*/ int main(){ int a[5]={32,44,55,75,23}; cout<<"a="<<a<<endl; cout<<"&a="<<&a<<endl<<endl; for (int i=0;i<5;i++){ cout<<"&a["<<i<<"]="<<&a[i]<<endl; } return 0; } ``` :::success OUTPUT: a= a5e0 &a=a5e0 &a[0]=a5e0 &a[1]=a5e4 ↲+4 bits &a[2]=a5e8 ↲+4 bits &a[3]=a5ec ↲+4 bits &a[4]=a5f0 ↲+4 bits ::: <br> + 利用指標,以下兩種陣列取值的方式是等價的 + <span class="pgreen">*a[i]* ≡* *(a+i)*</span> + 左邊為陣列寫法,右邊則是把陣列名稱當成指標 ```cpp= /*9.4.1指標常數的值與位址*/ int main(){ int a[3]={324,467,455,}; cout<<"a[0]="<<a[0]<<", *(a+0)="<<*(a+0)<<endl; cout<<"a[1]="<<a[1]<<", *(a+1)="<<*(a+1)<<endl; cout<<"a[2]="<<a[2]<<", *(a+2)="<<*(a+2)<<endl; return 0; } ``` :::success OUTPUT: a[0]=324, *(a+0)=324 a[1]=467, *(a+1)=467 a[2]=455, *(a+2)=455 ::: <br> ### 3.2 運用指標傳遞一維陣列進入函數中 <span class="pblue">範例程式</span> ```cpp= /*9.4.3 運用指標傳遞一維陣列進入函數中*/ void replace(int *, int, int); int main(){ int arr[5]={1,2,3,4,5}; int num=111; cout<<"Before replace, arr is "; for (int i=0;i<5;i++){ cout<<arr[i]<<" "; } cout<<endl; replace(arr,4,num); //第4項換成num(111) cout<<"After replace, arr is "; for (int i=0;i<5;i++){ cout<<arr[i]<<" "; } cout<<endl; return 0; } void replace(int * ptr, int n, int num){ /*<---------*/ //置換陣列的第n項為num *(ptr+n-1)=num; /*<---------*/ return; } ``` :::success OUTPUT: Before replace, arr is 1, 2, 3, 4, 5, After replace, arr is 1, 2, 3, 111, 5, ::: <br> + 傳回值為指標用來找出陣列中的最大值 <span class="pblue">範例程式</span> ```cpp= /*9.4.3 運用指標傳遞一維陣列進入函數中*/ int * maxima (int *); //傳回值為一整數指標,輸入值為一整數指標(array) int main(){ int a[5]={6,2,7,1,8}; int * ptr; cout<<"Array is "; for (int i=0;i<5;i++){ cout<<a[i]<<", "; } cout<<endl; ptr=maxima(a); cout<<"The maxima is "<<*ptr<<endl; return 0; } int * maxima (int * arr){ int * max; max=arr; //設定MAX初始值為第一位 for (int i=1;i<5;i++){ if(*max<*(arr+i)) //後數大於前數 max=arr+i; //就把它的指標交換 } return max; } ``` :::success OUTPUT: Array is 6, 2, 7, 1, 8, The maxima is 8 ::: <br> ## 12-4 指標與字串 Pointer and String ## 12-5 二維陣列與雙重指標 2-D Array and Pointer to Pointer ### 5.1 雙重指標 ![](https://hackmd.io/_uploads/r1XJ9lBTh.png =90%x) + 宣告格式 + <span class="program">*資料型態 ** 雙重指標*</span> ### 5.2 二維陣列與雙重指標之間的關係 ![](https://hackmd.io/_uploads/S1VZxWra3.png) <span class="pblue">範例程式</span> ```cpp= /*10.1 二維陣列與雙重指標之間的關係*/ int main() { int num[3] [4]; cout<<"num= "<<num<<endl; cout<<"*num= "<<*num<<endl; cout<<"&num= "<<&num<<endl; cout<<endl; cout<<"num[0]= "<<num[0]<<endl; cout<<"num[1]= "<<num[1]<<endl; cout<<"num[2]= "<<num[2]<<endl; cout<<endl; cout<<"&num[0]= "<<&num[0]<<endl; cout<<"&num[1]= "<<&num[1]<<endl; cout<<"&num[2]= "<<&num[2]<<endl; } ``` :::success OUTPUT: num= bce0 *num= bce0 &num= bce0 num[0]= bce0 num[1]= bcf0 num[2]= bd00 &num[0]= bce0 &num[1]= bcf0 &num[2]= bd00 ::: <br> ### 5.3用指標表示二維陣列各項 + *arr\[m]\[n] $\Longleftrightarrow$\*(\*(arr+m)+n)* ![](https://hackmd.io/_uploads/HkqaaP8Tn.png) <span class="pblue">範例程式</span> ```cpp= int main() { int arr[3][4]={{12,22,33,55}, {66,51,42,13}, {26,81,72,13}}; for (int i=0;i<3;i++){ for(int j=0;j<4;j++){ cout<<"arr["<<i<<"]["<<j<<"]= "<<*(*(arr+i)+j); cout<<",位址="<<*(arr+i)+j<<endl; }cout<<endl; } return 0; } ``` ::: success OUTPUT: arr[0][0]= 12,位址=07c90 arr[0][1]= 22,位址=07c94 arr[0][2]= 33,位址=07c98 arr[0][3]= 55,位址=07c9c arr[1][0]= 66,位址=07ca0 arr[1][1]= 51,位址=07ca4 arr[1][2]= 42,位址=07ca8 arr[1][3]= 13,位址=07cac arr[2][0]= 26,位址=07cb0 arr[2][1]= 81,位�=07cb4 arr[2][2]= 72,位址=07cb8 arr[2][3]= 13,位址=07cbc ::: <br> ## 12-6 動態記憶體配置 Dynamic memory allocation "靜態記憶體" : int, float, char, double, bool.... ### 6.1使用基本資料型態做動態配置 + 使用 new & delete 可以配置動態記憶體 + new: + *<span class="program">Type * ptr;</span>* //宣告指標 + *<span class="program">ptr = new type;</span>* //配置一個新的記憶體位置給他 + delete: + *<span class="program">delete ptr;</span>* //刪除指標記憶體位置 <span class="pblue">範例程式</span> ```cpp= /*10.2.1使用基本資料型態做動態配置*/ int main() { int * a; a = new int; //<<<<<<<<<<<<<<< *a =5; cout<<"*a="<<*a<<endl<<endl; cout<<*a<<"*"<<*a<<"="<<(*a)*(*a)<<endl<<endl; delete a; cout<<"*a="<< *a<<endl; //他就會不知道飛去哪裡 a = NULL; //安全起見把它指向NULL,沒有其實沒差 return 0; } ``` ::: success OUTPUT: *a=5 5*5=25 *a=1552478392 $\longleftarrow$飛走了 ::: ### 6.2使用陣列做動態配置 最常使用動態記憶體配置的資料型態就是陣列 + *<span class="program">Type * ptr;</span>* + *<span class="program">ptr = new type \[陣列大小];</span>* + *<span class="program">delete \[] ptr;</span>* 裡面沒有東西 + *<span class="program">ptr = NULL;</span>* 保證指標的安全性 <span class="pblue">範例程式</span> ```cpp= /*10.2.2使用陣列做動態配置*/ int main() { int * a; a = new int[5]; for (int i =0;i<5;i++) a[i]=i*2; for (int i =0;i<5;i++) cout<<"a["<<i<<"]= "<<a[i]<<"\t"; cout<<endl; delete [] a; a = NULL; return 0; } ``` ::: success OUTPUT: a[0]= 0 a[1]= 2 a[2]= 4 a[3]= 6 a[4]= 8 ::: <br> <span style="font-size:20px;color:#ff44ff">範例程式Hard</span> ```cpp= /*10.2.2使用陣列做動態配置*/ char * Set_string(char *); int main() { char * str; str = Set_string("Hello, Warren! "); cout<<str<<endl; delete [] str; return 0; } char * Set_string(char * text){ char * ptr; ptr = new char[strlen(text)+1]; strcpy (ptr,text); return ptr; } ``` ::: success OUTPUT: Hello, Warren! ::: ## 12-7 指標、參照與函數 Pointer, Reference, and Function ## 參考資料 [何謂指標? 指標存取與記憶體配置的基本觀念](https://www.youtube.com/watch?v=mNLzUlcL5rM) [CPP Road](https://cpproadadvanced.blogspot.com/) [紫色大磚塊](https://www.tenlong.com.tw/products/9789577179371)