###### tags: `MDCPP講義`
# 指標(pointer)
**Author: 謝侑哲**
----
在計算機科學中,指標是一個讓電腦運行的核心技術
----
指標本身是一段小小的記憶體
裡面**儲存了某個記憶體的位址**
----
這是我們上次看過的硬碟圖片

可以想像指標就像右邊HDD上面的那根針
----
我們可以透過指標來確定我們的變數、陣列...
總之就是所有資料的位址,並利用這個位址取出我們的資料
----
先帶一點指標的概念
不過我們在講指標之前,有些重要的東西要先提
---
## 位元(bit)
----
位元全名是Binary digit
至於甚麼是位元
``01``
這就是位元
----
我們常說程式就是010101組成的
事實上也是如此沒錯
----
先來看看我們之前教到的變數是怎麼由01組出來的吧

----
首先看到整數的部分(這張圖有誤,應該不是32而是31)
可以看見int的範圍是$2^{31}+2^{31}=2^{32}$

想想如果他有$2^{32}$種可能數字,我們透過01來排列組合
需要用到多少個01才能表達所有數字呢
答案顯然是$log_2(2^{32})=32$
所以表達一個int所需的空間就是32個bit
這也就是怎麼用bit來表達整數的概念
----
不過我們這裡用一個C++裡面的sizeof函式看看
這個函式可以告訴我們一個東西佔的記憶體大小是多少
```cpp=
int a;
cout<<"int size= "<<sizeof(a)<<endl;
```
```bash=
int size= 4
```
4? 奇怪了不是應該是32嗎
----
這邊要帶出另外一個很重要的概念
----
位元組(byte)
----
所謂位元組就是8個一組的bit
----
在計算機科學上我們更常使用位元組byte
來表示,畢竟如果出現一些單個單個的垃圾bit可能會影響記憶體的存取
也可以讓電腦先預留好一定的位置
當我們遇到把一個變數從8(3bit)改成128(8bit)的時候
就不用重新分配一次記憶體
----
所以說
```cpp=
int a;
cout<<"int size= "<<sizeof(a)<<endl;
```
```bash=
int size= 4
```
這段程式碼所輸出的4其實是4個位元組,也就是32bit
----

----

---
## 記憶體位址
----
回到記憶體位址
先來看看記憶體位址長怎樣
----
``0x00007fffffffe658``
這是我隨便抓的一段記憶體位址
我們一步步解析他
----
``0x00007fffffffe658``
先看前面0x的部分
這個0x是一種數學的表示方式
代表後面的數字是16進制(hex)
其他也有像是二進制(binary)的表示
開頭會用0b等等
----
``0x00007fffffffe658``
這邊我們可以看到記憶體的數字總長度是16
前面講過他是16進制,所以她總共可表示的數字就是
$16^{16} = 2^{64}$ 是64bit,也就是8byte
所以我們可以看出指標的大小是8byte
----
不過這個指標的大小在每台電腦上不一定一樣
回顧一下之前講到的

電腦有分32位元跟64位元
剛剛提的指標是64位元電腦的指標,也是最常見的
如果是32位元,則是只有4byte
----
為了讓某些程式在64位元跟32位元都可以跑
會保留一些兼容的操作
像是乘法的時候,如果原本是4byte的數字當她產生進位到第5byte時
會被丟到另一個區段,而不是塞成一個完整的5byte區段給指標
否則在32位元的電腦會爆掉
----
現在了解了位元跟記憶體位址,讓我們進下一步吧
---
## 指標
----
剛剛有講到指標是指向某個記憶體位址
但他只向的是甚麼記憶體位址呢?
----
指標通常指向的東西為一段記憶體的開頭

像這樣,變數b的那段記憶體被指針指到的地方就是
0x0012FF74
----
另外有一個很毒瘤的地方
假設存了3553145145這樣的數字
換成16進位是比較好看 d3c8b139
那他的儲存方式會是這樣
($16*16=2^8=1 byte$)
0x0012FF74 39
0x0012FF75 b1
0x0012FF76 c8
0x0012FF77 d3
這樣倒著一塊一塊存的
----
現在我們要來正式講指標在C++裡面怎麼寫拉
----
## C++裡的指標
----
先介紹兩個運算子
----
取址運算子(&)
----
我們一般取變數的時候
都是直接取得他的值
```cpp=
int a=1;
cout<<a;
```
```bash=
1
```
----
不過實際上我們取得值的方式是
先抓到他的記憶體位址
在透過變數名稱取得記憶體位址
用記憶體位址拿出他的數值
不過為了方便,程式通常直接讓我們取得值
----
所以這裡要帶出變數有三個要素
int b=1;
- 位址 0x0012FF74
- 值 1
- 名稱 b
所以實際的流程是 名稱->位址->值
----
所謂取址運算子,就是取得變數位址的一個運算子
他的符號是 &
----
用法是這樣
```
#include<bits/stdc++.h>
using namespace std;
signed main(){
int b=1;
cout<<"位址 "<<&b<<endl;
cout<<"值 "<<b<<endl;
}
```
```bash=
位址 0x61fe1c
值 1
```
----
間接取值運算子(*)
----
間接取值運算子
就是取址運算址的相反
可以從某個位址取得那個位址的值
符號是 *
----
用法是這樣
```
#include<bits/stdc++.h>
using namespace std;
signed main(){
int b=1;
cout<<"位址 "<<&b<<endl;
cout<<"值 "<<*&b<<endl;
}
```
```bash=
位址 0x61fe1c
值 1
```
----
另外指標這個東西是也可以宣告成一個變數的
只不過會變成他的值存了一個指標的位址
----
看看變數三要素
- 位址 0x0012FF74
- 值 0x0012FF08
- 名稱 b
----
看起來很奇怪,不過事實上這是完全可行的
因為我們在記憶體中,所存的值都是一堆數字
事實上並不具特別意義
所以0x0012FF08也是可以當成一個int用
重要的是我們透過甚麼方法去取他
----
用法是這樣
```
#include<bits/stdc++.h>
using namespace std;
signed main(){
int b=1;
int* b_pointer=&b;
cout<<"位址 "<<b_pointer<<endl;
cout<<"值 "<<*b_pointer<<endl;
}
```
```bash=
位址 0x61fe1c
值 1
```
{"metaMigratedAt":"2023-06-17T02:16:39.175Z","metaMigratedFrom":"Content","title":"指標(pointer)","breaks":true,"contributors":"[{\"id\":\"f547d745-63f3-4bad-986b-1751eeec19d1\",\"add\":3632,\"del\":244}]"}