---
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>

<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>

<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 函數指標

<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 傳遞函數指標到其他函數中

<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 雙重指標

+ 宣告格式
+ <span class="program">*資料型態 ** 雙重指標*</span>
### 5.2 二維陣列與雙重指標之間的關係

<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)*

<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)