追蹤

宗宗大學

喜歡關注科學、哲學、社會、政治、教育,未來想當一位抱著理想的科學家,期許社會能夠進步。

[筆記]陣列與指標|C++


我以為我傳的是陣列,但居然傳的是指標?為什麼我無法回傳陣列?只能回傳指標?所以陣列和指標是什麼關係?指標運算怎麼算?釋放動態記憶體時,指標變數會不會也被釋放?

這是一篇中山資工所 江明朝老師 物件導向程式設計課程筆記,如內容有錯,歡迎留言交流~

還是很困惑嗎?文章裡有答案喔~😎

ㄧ、陣列 (Array)

原始碼是一個陣列,但編譯後就變成指標,所以陣列和指標到底有什麼關係?

(一)指標與陣列的關係

  • int a[N]
  • a[i] == *(a+i) // Get value
  • &a[i] == &*(a+i) == a+i // Get address
  • The name of an array is a constant pointer pointing to the first element of the array.
  • a[0] == *(a+0) == *a // Get value
  • &a[0] == &*(a+0) == a // Get address

a其實是一個指向第一元素的常數指標,i為index,N是 one past the end,最後一個元素後一個位置。

 

  • typeof(a) == int[N]
  • sizeof(a) == N*sizeof(int)

如果想要弄一個index為1開始的陣列,該如何實作?

int a[10]; int *p = a -1; // p[1] == a[0]

建議陣列的大小一開始先定義在一個常數變數上,未來要更動陣列大小時,就會比較方便!!

更多指標與陣列的關係例子,請參考期中考第2和5題

 

(二)傳陣列

1. 傳陣列參數

雖然陣列名稱是一個指標變數,但還是有點不一樣:

 

  1. void func(int b[]){cout << sizeof(b) << endl;} // sizeof(int)*1,b是一個指標 --(1)
  2. int a[10]; cout << sizeof(a) << endl; // sizeof(int)*10,a是一個陣列 -- (2)

 

例子(1)可以發現,傳陣列給func()中,但引數b的大小是sizeof(int)*1,所以傳陣列給函式時,我們傳的是指標。例子(2)可以發現,直接檢查陣列a的大小,會顯示sizeof(int)*10,因此如果再呼叫func(a);,會顯示sizeof(int)*1。

從例子(1)可知,傳陣列給函數,會以指標的方式傳給函數,因為這樣會比較有效率。在函數存取時,可以使用陣列的[]存取,也可以使用指標的*存取。

void function (int a[]); == void function (int *a); // int a[] == int *a for passing arguments

2. 回傳陣列

那如果要回傳陣列出來呢?此時就不能直接回傳陣列,而是要回傳指標,因為我們傳給函式的陣列,其實是一個指標,而非陣列,因此回傳時也要回傳指標出來。

 

  • int [] someFunction(int a[]); // 非法,不能回傳陣列
  • int* someFunction(int a[]); // 合法,可以回傳指向陣列的指標,但不要回傳區域變數

 

另一種回傳陣列的方法是,將陣列包裹在struct / class中。

3. 陣列讀取

陣列的讀取有分by row或是by column,而為什麼C++函式參數列中,陣列的第一維可以不用給數字?

因為不需要第一維的數字,C++知道第二維長度就可以自動判斷出第一維長度。例如一個3x4的a陣列,傳陣列參數時,只要寫a[][4]即可。C++知道這是一個12大小,第二維為4的陣列,自然第一維就是3。

int a[2][5]; a[0][5] == a[1][0];?

complie error,不一定總是會成立,因為可能有些設計是by row或是by column

(三)陣列的指標運算

在陣列中,byte和element要有所區分,因為byte == sizeof(element),byte會隨著陣列裝的東西有關,但index不會。byte和element的區分,在指標運算中很重要。如果只是單一指標和常數的四則運算,這是記憶體位置的運算,因此和byte有關;而如果是指向同陣列的兩指標做加減,這是兩指標距離的運算,因此和element有關。

更多陣列的指標運算的例子,請參考期中考第3和4題

二、指標 (Pointer)

(一)指標變數

指標為存放記憶體位址的變數型態!指標是位址,位址是整數,但指標不是整數!

 

  • int i = 10; void *p = &i; // 正確,pointer to void可以接受任何型態
  • int *q = p; // 錯誤,pointer to int無法直接接受pointer to void
  • int *q = (int*)p; //正確,必須使用casting將之轉成pointer to int
  • int*p1, *p2, v1, v2; //盡量不要這樣宣告,容易混淆,p1和p2為int*,而v1, v2為int
  • p2 = p1; // p2和p1所存放的值(位址)一樣,也就是p1和p2指向同一個位址
  • *p1=*p2; //p2指向的物件值,給予p1指向的物件

 

(二)指標與const

如果參數不必修改,建議多使用const。

 

  1. 比較不會出錯
  2. 編譯器可以對此優化

 

__1__ int * __2__ ptr; // 在讀C++的型態時,由後往前讀!!

  1. ____, ____:pointer to int,指標與物件皆可更改。
  2. const, ____:pointer to const int,指標可改,物件不可改,常用於傳遞參數。
  3. ____, const:const pointer to int,指標不可改,物件可改,例如陣列。
  4. const, const:const pointer to const int,指標與物件皆不可更改,很像參考 (reference)。

__1__ int __2__ * __3__ ptr; // __1__ == __2__:兩者位置皆相同

 

(三)動態記憶體

 

  1. int *p1 = new int(2); delete p1; p1 = nullptr; // 配置一個整數2給p1
  2. int* p2 = new int[5]; delete[] p2; p2 = nullptr; // 配置一個5個元素的空陣列給p2

 

剛new出來的東西是沒有名字的,new出來的東西位址要存放在指標中,用這個指標去存取這個新東西。new除了可以配置動態記憶體外,也可以做初始化的動作。

如果new後沒有delete,可能會造成記憶題遺失,所以使用new,就一定要使用delete!然後delete是釋放指標所指向的物件,而非指標本身被釋放!因此被釋放的空指標仍可以重複再指向新的物件。建議釋放記憶體後,要給指標接上nullptr,避免重複釋放記憶體,重複釋放記憶體會造成錯誤。但如果多多使用STL,就不會造成記憶題遺失,因為它會自動幫你配置予釋放記憶體。如:C++ <vector>。

三、相關文章


本文章發表於:課程

加入85

宗宗大學

國立中山大學 生物科學系

追蹤 384 鼓勵作者

喜歡關注科學、哲學、社會、政治、教育,未來想當一位抱著理想的科學家,期許社會能夠進步。

鼓勵作者

目前持有 Blink Coin: Loading..

選擇禮物


愛心

(Coin 10)

幫高調

(Coin 20)

咖啡

(Coin 30)

掌聲鼓勵

(Coin 40)

崇拜眼神

(Coin 50)

驚呆了

(Coin 60)

神人4ni

(Coin 70)

花束

(Coin 100)

鑽石

(Coin 300)

紅寶石

(Coin 500)

藍寶石

(Coin 1000)

黃寶石

(Coin 3000)


送出鼓勵



發表匿名文章不會出現你的大頭圖與名稱,你可暢所欲言,但文章內容務必遵守「佈告欄使用規範」!


回應

送出回應


想回應這篇文章嗎?也想發表文章嗎?
馬上登入來發表文章、追蹤作者、收藏文章或回應文章吧!

註冊 登入