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

2021年7月6日 21:17
我以為我傳的是陣列,但居然傳的是指標?為什麼我無法回傳陣列?只能回傳指標?所以陣列和指標是什麼關係?指標運算怎麼算?釋放動態記憶體時,指標變數會不會也被釋放? 這是一篇中山資工所 江明朝老師 物件導向程式設計課程筆記,如內容有錯,歡迎留言交流~ 還是很困惑嗎?文章裡有答案喔~😎 好讀版:
ㄧ、陣列 (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函式。
12
留言 3
文章資訊
85 篇文章294 人追蹤
Logo
每天有 10 則貼文
共 3 則留言
國立中山大學
用心❣️❣️
國立中山大學 資訊工程學系
(二).3 的 by row 跟 by column 有專有名詞:row major, column major 然後這超級不 C++,這些大多可以原封不動照搬到 C 語言去。 Modern C++ 告訴我們,少直接碰那些指標,請愛用 smart pointer, iterator, 或 ranges(C++20) STL 也有封裝陣列,叫做 std::array<T, N> 這有兩大好處: 1. 替換容器介面不變 2. 陣列宣告必須 compile time 給定大小,避免你直接 out of range 所以,在這片筆記中所介紹的偏 C,C++ 建議的已經不一樣 SCC
國立中山大學
好久沒寫了,現在STL有那麼先進了喔(?)