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

2021年7月6日 21:20
我以為我傳的是陣列,但居然傳的是指標?為什麼我無法回傳陣列?只能回傳指標?所以陣列和指標是什麼關係?指標運算怎麼算?釋放動態記憶體時,指標變數會不會也被釋放? 這是一篇中山資工所 江明朝老師 物件導向程式設計課程筆記,如內容有錯,歡迎留言交流~ 還是很困惑嗎?文章裡有答案喔~😎 好讀版:
ㄧ、陣列 (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. 傳陣列參數 雖然陣列名稱是一個指標變數,但還是有點不一樣: void func(int b[]){cout << sizeof(b) << endl;} // sizeof(int)*1,b是一個指標 --(1) 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__ int * __2__ ptr; // 在讀C++的型態時,由後往前讀!! ____, ____:pointer to int,指標與物件皆可更改。 const, ____:pointer to const int,指標可改,物件不可改,常用於傳遞參數。 ____, const:const pointer to int,指標不可改,物件可改,例如陣列。 const, const:const pointer to const int,指標與物件皆不可更改,很像參考 (reference)。 __1__ int __2__ * __3__ ptr; // __1__ == __2__:兩者位置皆相同 (三)動態記憶體 int *p1 = new int(2); delete p1; p1 = nullptr; // 配置一個整數2給p1 int* p2 = new int[5]; delete[] p2; p2 = nullptr; // 配置一個5個元素的空陣列給p2 剛new出來的東西是沒有名字的,new出來的東西位址要存放在指標中,用這個指標去存取這個新東西。new除了可以配置動態記憶體外,也可以做初始化的動作。 如果new後沒有delete,可能會造成記憶題遺失,所以使用new,就一定要使用delete!然後delete是釋放指標所指向的物件,而非指標本身被釋放!因此被釋放的空指標仍可以重複再指向新的物件。建議釋放記憶體後,要給指標接上nullptr,避免重複釋放記憶體,重複釋放記憶體會造成錯誤。但如果多多使用STL,就不會造成記憶題遺失,因為它會自動幫你配置予釋放記憶體。如:C++ <vector>。 三、相關文章 1. 物件導向程式設計|模擬真實世界的方式|江明朝
江老師物件導向程設,評分、老師、課程簡介。 2. AOOP Homework source code
3. [筆記]109-2高等物件導向程式設計 期中考
4. [筆記]介面與實作、運算子多載、左值右值、參數傳遞、回傳多值|C++
運算子多載如何執行?int a = 10; ++a—;這串程式碼為什麼會出錯?居然跟左值又值有關!左值右值真的是一左一右嗎?參數傳遞中的傳指標和傳參考差在哪邊?C++居然無法回傳多值,那我該怎麼回傳多值? 5. [筆記]陣列與指標|C++
我以為我傳的是陣列,但居然傳的是指標?為什麼我無法回傳陣列?只能回傳指標?所以陣列和指標是什麼關係?指標運算怎麼算?釋放動態記憶體時,指標變數會不會也被釋放? 6. [筆記]類別、特殊函式、內嵌函式、函式物件|C++
程序導向的C語言也能實作物件導向?C語言要如何模擬類別?C struct與C++ struct與C++ class這三者有什麼差別?類別中有哪六個特殊函式?初始化和賦值有什麼差別?內嵌函式和巨集很像?物件居然可以拿來當成函式?為什麼C++宣告無參數物件時,不用加()? 7. [筆記]繼承模式與存取權限|C++
繼承模式和存取權限有什麼關係?private繼承不是讓所有存取權限變成private嗎?為什麼衍生類別依舊可以存取基礎類別?類別開發者和使用者有什麼區分?特殊函式的繼承會和一般函式一樣嗎? 8. [筆記]多型與繼承的關係|C++
多型的出現是為了要解決什麼問題?多型觸發的條件是什麼?為什麼多型要和繼承一起使用?多型能保有繼承的優點嗎?多型比繼承又多了什麼功能?特殊函式也能多型嗎? 9. [筆記]static / const成員資料與函式|C++
non-const static成員資料無法在類別內初始化,也無法使用初始化列,那到底該如何初始化?static成員是什麼概念?可以將自己宣告為自己的成員資料嗎?如果可以,該如何實現?如果不行,會發生什麼問題? 10. [筆記]夥伴函式與類別、不夠朋友問題|C++
夥伴函式和成員函式差在哪裡?為什麼輸入/輸出的多載運算子一定要為夥伴函式?如果A類別把B函式當作夥伴,但B函式的參數中沒有A類別,顯然這個B不把A當夥伴,就會造成main()無法找到B函式。
27
留言 14
文章資訊
85 篇文章294 人追蹤
Logo
每天有 7 則貼文
共 14 則留言
國立臺灣科技大學
「陣列的大小一開始先定義在一個常數變數上」 那怎麼更改陣列的大小?
長庚大學
B1 他的意思應該是說 不要用 int arr[10]; 這種寫法 而是先 const int size = 10; 然後再 int arr[size]; 如此一來 如果想要把陣列大小改成 20 只要修改 size 的值即可 不需要一個一個把所有程式中出現的 10 都改成 20 像是 for 迴圈之類的如果都用字面常量 改起來很費時費力又容易漏
國立臺灣科技大學
回傳陣列用指標的話 要怎麼讓caller知道陣列的大小? 要是指標指的位置後面有其他資料 這樣可能會壓到喔
國立臺灣科技大學
B2 原來是這樣的意思 沒錯 用const int確實可以一次改很多地方 這樣我懂樓主的意思了
謝謝 b2 補充, b1 b2 所言,常數變數的用意並非讓陣列在run time中改變其大小,而是讓工程師維護比較方便。
b3 為什麼回傳陣列時,需要讓caller知道大小? 你說的壓到,是指超出陣列的範圍嗎?
國立臺北科技大學
B3 我沒有寫過C++ 都是在寫C比較多 既然是你宣告array那代表你知道 array的size吧 沿用原PO說的 那function這樣定義 int * createarray(int *arraysize) { const int size = 10; int *arr=malloc(sizeof(int)*size ); *arraysize=size; return arr; } 這樣就可以從return得到陣列 然後透過參數去取出size 如果有更好的方法再麻煩大家提出
國立臺灣科技大學
B6 對 超過陣列的範圍 會發生問題
國立中央大學 資訊工程學系
B8 有一點比較tricky的方法 假設已經得知該array是放在stack或是bss/data區段 可以用sizeof來取得該array的大小 所以長度可以用sizeof(array)/sizeof(該elements) 來取得 這是因為sizeof本質上是compile time決定的 所以compile的時候已經把所有關於sizeof的資訊取代掉 而array的大小恰好是compile time已知 所以可以用這招來知道array長度
國立臺灣科技大學
B7 瞭解 用int* arraysize來傳遞給caller 這樣caller就知道了~
國立臺北科技大學
B10這是C的寫法 C++應該還有更好的方式來解決才對XD 另外也可以這樣寫 int * createarray(int arraysize) { int *arr=malloc(sizeof(int)*arraysize); return arr; } 這樣也可以 畢竟你知道你要create多大的陣列了 那代表caller要傳入的參數就是你決定了 這樣寫應該會比較符合應用
國立臺灣科技大學
B11 c++會怎麼解決呢?
長庚大學
B12 C++ 比較不會用這種指標陣列吧 要嘛改用 std::array 之類的 standard container 要嘛改用 smart pointer
國立成功大學
b2 用const int 的話還能修改size的值嗎?