【C++ 筆記】指標(Pointer)(下) - part 13 === 目錄(Table of Contents): [TOC] --- 很感謝你點進來這篇文章。 你好,我並不是什麼 C++、程式語言的專家,所以本文若有些錯誤麻煩請各位鞭大力一點,我極需各位的指正及指導!!本系列文章的性質主要以詼諧的口吻,一派輕鬆的態度自學程式語言,如果你喜歡,麻煩留言說聲文章讚讚吧! 指標與陣列(Pointer vs. Array) --- 指標與陣列其實是有相關性的,先從定義上來看: :::success 陣列的定義: 陣列是一組相同型態的元素,這些元素在記憶體中是連續儲存的。陣列的名稱可以視為指向其首項元素的指標。例如,對於一個整數陣列 int arr[5];,arr 實際上是指向 arr 的指標。 ::: :::info 指標的定義: 指標是一種變數,用來儲存其他變數的位址。指標可以指向任何型態的資料,包括陣列。 ::: > 指標和陣列在很多情況下是可以互換的。例如,一個指向陣列開頭的指標,可以透過使用指標的算術運算或陣列索引來存取陣列。請看下面的程式: ```cpp= #include <iostream> using namespace std; const int MAX = 3; int main () { int var[MAX] = {10, 100, 200}; int *ptr; // 指標中的陣列位址 ptr = var; for (int i = 0; i < MAX; i++) { cout << "var[" << i << "] 的記憶體位址為 "; cout << ptr << endl; cout << "var[" << i << "] 的值為 "; cout << *ptr << endl; // 移動到下一個位置(索引) ptr++; } return 0; } ``` 輸出結果: ``` var[0] 的記憶體位址為 0x61fe08 var[0] 的值為 10 var[1] 的記憶體位址為 0x61fe0c var[1] 的值為 100 var[2] 的記憶體位址為 0x61fe10 var[2] 的值為 200 ``` 來源:https://www.runoob.com/cplusplus/cpp-pointers-vs-arrays.html 以上範例即將指標遞增,具有與 i 相同的功能,能夠遍歷整個陣列。 > 然而,指標和陣列並不是完全互換的。例如,請看下面的程式: ```cpp= #include <iostream> using namespace std; const int MAX = 3; int main () { int var[MAX] = {10, 100, 200}; for (int i = 0; i < MAX; i++) { *var = i; // 這是正確的語法 var++; // 這是不正確的 } return 0; } ``` > 把提取(Dereference)運算子 `*` 應用到 `var` 是完全可以的,但修改 `var` 的值是非法的。這是因為 var 是指向陣列開頭的**常數**,不能作為左值(lvalue)。 :::success 左值(lvalue)和右值(rvalue): ```cpp= int x = 0; ``` 右值為 `0`,(專業術語為:literal constant -> 字面常數),而變數 `x` 為左值。 左值(lvalue):任何有記憶體位址的變數都是左值,包括不可修改的 const 變數。 右值(rvalue):非左值即右值,通常也是運算式所產生暫時的值。 ::: > 由於一個陣列名稱對應一個指標常數,只要不改變陣列的值,仍然可以用指標形式的運算式。例如,下面是一個有效的語句,把 var[2] 賦值為 500: ```cpp= *(var + 2) = 500; ``` 指標型陣列(Array of Pointers) --- 以下是指標陣列的宣告方式: ```cpp= int *ptr[MAX]; ``` 以上是一個指標陣列,長度共有 MAX 個,裡面所有的元素皆指向整數型態。 你可能會很疑惑,為何還需要指標陣列呢?原因是他有以下這五大優點: 1. 靈活的記憶體管理 2. 簡化多維資料結構的處理 3. 提升效能 4. 簡化函數參數傳遞 5. 實現複雜資料結構 以下是一個範例: ```cpp= #include <iostream> using namespace std; int main() { // 定義一個整數陣列 int numbers[] = {10, 20, 30, 40, 50}; // 計算陣列的大小 int size = sizeof(numbers) / sizeof(numbers[0]); // 定義一個指標陣列,指向整數 int* pointers[size]; // 將每個指標指向整數陣列中的元素 for (int i = 0; i < size; ++i) { pointers[i] = &numbers[i]; } // 輸出每個指標所指向的值 cout << "Values in the array using pointer array:" << endl; for (int i = 0; i < size; ++i) { cout << "Value at pointers[" << i << "] = " << *pointers[i] << endl; } return 0; } ``` 輸出結果: ``` Values in the array using pointer array: Value at pointers[0] = 10 Value at pointers[1] = 20 Value at pointers[2] = 30 Value at pointers[3] = 40 Value at pointers[4] = 50 ``` 以下是一個指向字元的指針陣列,用以儲存字串列表的範例: ```cpp= #include <iostream> using namespace std; const int MAX = 4; int main (){ const char *names[MAX] = { "C++ is best.", "I am handsome!", "Haha welcome to programming world.", "good night.", }; for (int i = 0; i < MAX; i++) { cout << "Value of names[" << i << "] = "; cout << names[i] << endl; } return 0; } ``` 輸出結果: ``` Value of names[0] = C++ is best. Value of names[1] = I am handsome! Value of names[2] = Haha welcome to programming world. Value of names[3] = good night. ``` 雙重指標(Pointer to Pointer / Double Pointer) --- 我們都知道指標是一個變數,用於儲存另一個變數的記憶體位址。 那麼雙重指標就是一個指標指向另一個指標,那麼所儲存的也就是指標的記憶體位址。 語法是加上兩個星號,如下: ```cpp= int **var; ``` 以下是一個範例: ```cpp= int value = 100; int *ptr = &value; // 單層指標,指向整數值 int **doublePtr = &ptr; // 雙層指標,指向單層指標 ``` 那他有什麼用呢?請看以下三點: 1. 動態記憶體管理:雙重指標常用於動態分配二維陣列或其他複雜資料結構。由於需要管理多個指標,使用雙重指標可以方便地處理記憶體分配和釋放。 2. 函數參數傳遞:當需要在函數中修改指標本身時,雙重指標非常有用。使其可在函數內部改變傳入的指標,指向新的記憶體位址。 3. 處理複雜資料結構:雙重指標也適用於處理更複雜的資料結構,如鏈結串列、二元樹等。在這些結構中,常需要透過多層的 Dereference 來存取或修改節點。 再來練習一題!以下是一個範例: ```cpp= #include <iostream> using namespace std; int main () { int var; int *ptr; int **pptr; var = 3000; // 獲取 var 的地址 ptr = &var; // 使用運算子 & 獲取 ptr 的地址 pptr = &ptr; // 使用 pptr 獲取值 cout << "var 值為 : " << var << endl; cout << "*ptr 值為 : " << *ptr << endl; cout << "**pptr 值為 : " << **pptr << endl; return 0; } ``` 輸出結果: ``` var 值為 : 3000 *ptr 值為 : 3000 **pptr 值為 : 3000 ``` 函數回傳指標 --- 語法: ```cpp= int * myFunction() { . . . } ``` 此函數即表為可以回傳指標的函數。 > ***另外,C++ 不支援函數外回傳局域變數的位址,除非定義局域變數為 static 變數。*** 以下是一個範例: ```cpp= #include <iostream> using namespace std; // 函數回傳一個指向整數的指標 int* createNumber() { static int number = 42; return &number; } int main() { int* numberPtr = createNumber(); // 取得 createNumber() 之回傳值 // 使用回傳的指標 cout << "The number is: " << *numberPtr << endl; return 0; } ``` 輸出結果: ``` The number is: 42 ``` 總結 --- 指標與陣列息息相關,可透過指標對陣列做出一些運算。最常見就是拿指標遍歷一個陣列。 以下是指標陣列的宣告方式:`int *ptr[MAX];` 雙重指標即加上兩顆星星:`int **var;` C++中可以在函數裡面回傳指標。 以上,結束。 參考資料 --- [C++ Pointer To Pointer (Double Pointer) - GeeksforGeeks](https://www.geeksforgeeks.org/cpp-pointer-to-pointer-double-pointer/) [C/C++ 中的雙指標](https://codelove.tw/@tony/post/8xXwAq) [指標的指標](https://openhome.cc/Gossip/CGossip/Pointer2Pointer.html) [C++ 左值與右值 - HackMD](https://hackmd.io/@23657689/cpp_lvalue_rvalue) [C++ 指针 vs 数组 | 菜鸟教程](https://www.runoob.com/cplusplus/cpp-pointers-vs-arrays.html) [C++ 指针数组 | 菜鸟教程](https://www.runoob.com/cplusplus/cpp-array-of-pointers.html) [C++ 指向指针的指针(多级间接寻址) | 菜鸟教程](https://www.runoob.com/cplusplus/cpp-pointer-to-pointer.html) [C++ 从函数返回指针 | 菜鸟教程](https://www.runoob.com/cplusplus/cpp-return-pointer-from-functions.html)