Try   HackMD
tags: 大一程設-下 東華大學 東華大學資管系 基本程式概念 資管經驗分享

指標 pointer 基礎認識(二)

前言

請確定(一)你讀懂了哦!!

之所以會有這篇(二)是因為想要配合講義的順序,主要是想要說明一下 new 跟 delete,其實不應該這麼快,但老師都按照講義教,所以只好先說明一下。

那在之前有說過像陣列或是 vector 必須要找到一塊足夠大且連續的記憶體才能夠被儲存,而這樣其實是有點沒效率的,所以之所以要學 new/delete 跟 pointer 的原因就是,我們可以親自一個一個地來配置這些記憶體,完全的利用我們的記憶體。提高記憶體的利用率。

講解影片

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

動態配置記憶體

靜態(static)配置記憶體

以前我們所宣告的變數或陣列,其所需要的記憶體是在程式編譯(compile)時所決定,並在程式一執行時做配置(就是你們寫好程式,按下執行的那刻開始)。

但這樣配置的空間大小都需要明確指定,且固定不能更改。一開始教陣列就有跟你們說其實不行執行的時候輸入值給陣列大小。我們是偷吃步的!

不過總不能都靜態配置吧,一定有需求,我們可能會想要更多東西,所以當然有動態配置摟。

動態(dynamic)配置

動態配置是指記憶體配置是在程式執行的時候進行(可以想像你們執行程式的時候,會在黑框框一直輸入內容,就是那個時候)。

所以我們可以配置未定大小的陣列,根據需求在程式執行的時候配置就好,動態配置提供給我們相當高的彈性。

舉個例來說,今天有非常多個班級,每一班都要做成績的儲存,然而每個班的人數都不同,總不能配一個很大的陣列,這樣一定會有資源的浪費,所以動態配置就可以根據班級各自的需求,配置相對應容量的記憶體,這樣就很有效率。

new - 動態配置記憶體

而既然都說是動態配置,是由程式指令來配置記憶體,需要我們自己去撰寫,什麼時候要配,什麼時候要刪掉,這些需要我們自己透過程式碼來達到。

那針對動態配置這件事情,就要利用 new 這個關鍵字。

只要看到 new 請你第一時間想到配出記憶體
請問是誰要來存記憶體?
指標變數!!!
Orange

所以希望你不要納悶為甚麼是在指標的後面做 new 的動作。

int *p1 = new int; // 宣告時配置記憶體 *p1 = 5; ------分隔線----- int *p1 = new int(5); // 宣告並配置記憶體同時附值 ------分隔線----- int *p1; // 先宣告 p1 = new int; // 再動態配置記憶體 *p1 = 5; // 再附值 ------分隔線----- int *p1; p1 = new int(5);

來看張圖吧,相同顏色的筆互相對應哦!

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

那個 0x8ffde0 就是 new int 所配置出來的記憶體,代表說這個被配置出來的記憶體裡面要存整數!所以附值就是指為指標變數儲存的位址設值。

  • 咖啡色的 cout
    • 取值(取得指標變數的值,記得我說過取值就打名字)
  • 紅色的 cout
    • 取址(取得指標變數在記憶體的位址)
  • 藍色的 cout
    • 取出指標變數所儲存的位址指向的值

這邊請你搞懂附值是什麼意思,到底是為誰附值,附去哪裏,還有疑問的話你必須看一下前一篇筆記哦。

看個小範例,請問他在做甚麼事呢?

int main(){ int *p1, *p2, x = 0; //宣告兩個指標變數與一個整數變數 //動態配置記憶體給指標變數 p1 並將其指向的位址的值設成 10 p1 = new int(10); //動態配置記憶體給指標變數 p2 並將其指向的位址的值設成 5 p2 = new int(5); cout << *p1 << " " << *p2 << endl; //這行會印出? //下面在做甚麼事呢? x = *p1; *p1 = *p2; *p2 = x; cout << *p1 << " " << *p2 << endl; //這行會印出? }

delete - 動態配置的,請你回收記憶體

講過了動態配置的記憶體是在你程式執行的時候根據程式碼去做配置的,而我們自己配出去的記憶體,電腦就不會幫我們管了,我們必須要自己去使用他,不用的時候要把他刪掉,否則會占用記憶體資源。

所以你應該理解,陣列因為是在靜態配置的時候程式一編譯好就配好了,電腦會自己去幫我們管理陣列的記憶體,不用的時候就釋放他。

但動態陣列就不同了,手動把用不到的記憶體刪掉很重要!!!

int main(){ double *p1, *p2; p1 = new double(5.3); p2 = new double(4.2); cout << *p1 + *p2 << endl; // 9.5 cout << *p1 - *p2 << endl; // 1.1 cout << *p1 * *p2 << endl; // 22.26 cout << *p1 / *p2 << endl; // 1.2619 delete p1; // p1 用不到了,釋放記憶體 delete p2; // p2 用不到了,釋放記憶體 return 0; }

釋放記憶體的語法很簡單,就只要 delete 你所配置的東西就好。

  • delete name;

小例子

請看以下程式碼。

int main(){ int *p1, *p2; p1 = new int; *p1 = 40; p2 = p1; cout << "*p1=" << *p1 << endl // 請問 *p1 是多少呢? << "*p2=" << *p2 << endl // 請問 *p2 是多少呢? << "p1=" << p1 << endl // 請問 p1 儲存的指標是? << "p2=" << p2 << endl; // 請問 p2 儲存的指標是? p1 = new int; *p1 = 80; cout << "*p1=" << *p1 << endl // 請問 *p1 是多少呢? << "*p2=" << *p2 << endl // 請問 *p2 是多少呢? << "p1=" << p1 << endl // 請問 p1 儲存的指標是? << "p2=" << p2 << endl; // 請問 p2 儲存的指標是? return 0; }

第 6 行會印出 40,第 7 行也會印出 40,你知道原因嗎?

因為我們把指標變數 p1 所儲存的值(指標) assign 給指標變數 p2,所以 p2 指標變數現在裡面存的指標跟 p1 指標變數存的指標(也就是記憶體位址)相同。

第 12 行會印出 80,第 13 行會印出 40,又是為甚麼呢?

  1. 還記得有說過 new 會配置一塊記憶體出來,所以在第 3 行我們 new 一次,配置了一塊記憶體給指標變數 p1 儲存。
  2. 接著在第 4 行為這個指標變數儲存的指標所指向的位址附值。
  3. 但在第 8 行我們又 new 了一次,因此配置一塊新的記憶體給指標變數 p1 這樣舊的就被替換掉了,換成新的一塊,接著再為指標變數 p1 所儲存的新的指標所指向的位址附值。
  4. 因此現在指標變數 p1 所儲存的指標跟指標變數 p2 所儲存的指標是不同的,因此附的值也是去不一樣的記憶體區塊。

你可以看到上圖,最後 p1 因為我們 new 了新的記憶體給他,所以他跟 p2 所儲存的指標(記憶體位址)有所不同。


但用指標實作一維陣列、二維陣列的 new 跟 delete 都會變形,我們到後面的筆記再來談!

Reference