【C++ 筆記】指標(Pointer)(上) - part 10 === 目錄(Table of Contents): [TOC] --- 很感謝你點進來這篇文章。 你好,我並不是什麼 C++、程式語言的專家,所以本文若有些錯誤麻煩請各位鞭大力一點,我極需各位的指正及指導!!本系列文章的性質主要以詼諧的口吻,一派輕鬆的態度自學程式語言,如果你喜歡,麻煩留言說聲文章讚讚吧! 指標(Pointer) --- 在講指標之前,我們都知道變數具有「值(value)」、「資料型態(Data Type)」、「記憶體位址(Memory Address)」三種特性。 我們要存取變數的記憶體位址,需要使用「&」符號加在變數名前面獲取,如下: ```cpp= &variable ``` 透過以下範例,可以知道變數的記憶體位址如何獲取: ```cpp= #include <iostream> using namespace std; int main(){ int a = 0; cout << &a; return 0; } ``` 輸出結果: ``` 0x7ffdd306c874 ``` :::success 問題來了,指標是啥呢? ::: 指標(Pointer),又稱為指針,是一個變數,可以用於儲存特定記憶體位址。 所以指標就是專門存記憶體位址的變數,存誰的?當然是其他變數的阿! 語法: ```cpp= type *ptr; ``` 註1:指標的資料型態無論如何都一樣,其記憶體位址都表示成十六進位。但不同資料型態的指標,差異是指標所指向的變數或常數的資料型態不同。 註2:提取(Dereference)運算子 `*`,用來提取指標儲存位址處的物件。 以下是一個範例: ```cpp= #include <iostream> using namespace std; int main() { int number = 10; // 整數型態變數 int* ptr = &number; // 指標 ptr 指向 number 的記憶體位址 // 顯示變數與指標的基本資訊 cout << "number 的值: " << number << endl; cout << "number 的記憶體位置: " << &number << endl; cout << "ptr 的值 (儲存的記憶體位置): " << ptr << endl; cout << "ptr 指向的值 (*ptr): " << *ptr << endl; // 用指標修改 number 的值 *ptr = 20; cout << "用指標修改後的 number 值: " << number << endl; return 0; } ``` 輸出結果: ``` number 的值: 10 number 的記憶體位置: 0x61fe14 ptr 的值 (儲存的記憶體位置): 0x61fe14 ptr 指向的值 (*ptr): 10 用指標修改後的 number 值: 20 ``` ### Null 指標(Null Pointer) --- 變數宣告時,若沒精確的地址可以指定,替指標變數賦予一個 NULL 值是良好的習慣。其名為空指標。 > NULL 指標是定義在標準函式庫中的值為零的常數。 如下範例: ```cpp= #include <iostream> using namespace std; int main () { int *ptr = NULL; cout << "ptr 的值是 " << ptr ; return 0; } ``` 輸出結果: ``` ptr 的值是 0 ``` 來源:https://www.runoob.com/cplusplus/cpp-null-pointers.html #### C++ NULL 指標應用 1. 初始化(Initialization):將指標初始化為空值(nullptr)是一個良好的習慣,因為這可以避免未定義行為,並**明確表示指標尚未指向有效的記憶體位置**。 2. 預設值(Default Values):當指標尚未分配有效的記憶體地址時,空指標可作為指標的預設值或初始值。 3. 錯誤處理(Error Handling):空指標在錯誤條件下或用於**表示資料缺失**時非常有用,有助於更好地處理例外情況。 4. 資源釋放(Resource Release):在釋放資源(例如類別的解構函數)或刪除後將指標設為空值(nullptr),可避免意外使用或存取已釋放的記憶體。 5. 哨兵值(Sentinel Values):空指標可以用來表示資料結構或清單的結尾。例如,在鏈結串列中,最後一個節點的「下一個節點」欄位通常是一個空指標。 #### C++ NULL 指標缺點 * 提取(dereference)空指標會導致未定義行為(undefined behavior),可能引發執行時錯誤,例如記憶體區段錯誤(segmentation fault)。 * 在提取空指標之前,需要明確檢查是否為空指標,才能避免未定義行為。 Source:https://www.geeksforgeeks.org/null-pointer-in-cpp/ 至於這點,可以透過條件判斷的方式避免此類型錯誤: ```cpp= if(ptr) /* 如果 ptr 非空,則完成 */ if(!ptr) /* 如果 ptr 為空,則完成 */ ``` 以下是菜鳥教程的解釋: > 在大多數的作業系統上,程式不允許存取位址為 0 的記憶體,因為該記憶體是作業系統保留的。然而,記憶體位址 0 有特別重要的意義,它表明該指標不指向一個可存取的記憶體位址。但按照慣例,如果指標包含空值(零值),則假定它不指向任何東西。 > 因此,如果所有未使用的指標都被賦予空值,同時避免使用空指標,就可以防止誤用一個未初始化的指標。很多時候,未初始化的變數存有一些垃圾值,導致程式難以調試。 ### 指標的算術運算(Pointer Arithmetic) --- > 指標是一個用數值表示的位址。因此,您可以對指標執行算術運算。指標可以進行四種算術運算:++、--、+、-。 #### 遞增指標範例 ```cpp= #include <iostream> using namespace std; int main() { int arr[] = {10, 20, 30, 40, 50}; int size = sizeof(arr) / sizeof(arr[0]); // 計算陣列的大小 // 定義指標指向陣列的第一個元素 int* ptr = arr; cout << "指標逐一存取陣列元素:" << endl; // 用指標逐一存取陣列元素 for (int i = 0; i < size; ++i) { cout << "元素 " << i << ": " << *ptr << endl; ++ptr; // 將指標遞增,指向下一個元素 } // 重新將指標指向陣列起始位置 ptr = arr; cout << "\n指標修改陣列元素的值:" << endl; // 用指標修改陣列元素的值 for (int i = 0; i < size; ++i) { *ptr += 5; // 將每個元素的值加 5 cout << "元素 " << i << " 新值: " << *ptr << endl; ++ptr; // 將指標遞增,指向下一個元素 } return 0; } ``` 輸出結果: ``` 指標逐一存取陣列元素: 元素 0: 10 元素 1: 20 元素 2: 30 元素 3: 40 元素 4: 50 指標修改陣列元素的值: 元素 0 新值: 15 元素 1 新值: 25 元素 2 新值: 35 元素 3 新值: 45 元素 4 新值: 55 ``` #### 遞增、遞減指標範例 ```cpp= #include <iostream> using namespace std; int main() { int numbers[] = {10, 20, 30, 40, 50}; int size = sizeof(numbers) / sizeof(numbers[0]); // 定義指標指向陣列的開頭 int* ptr = numbers; cout << "原始陣列:" << endl; for (int i = 0; i < size; ++i) { cout << numbers[i] << " "; } cout << endl; // 指標進行遞增操作 cout << "指標遞增存取陣列內容:" << endl; for (int i = 0; i < size; ++i) { cout << *ptr << " "; ++ptr; // 指向下一個元素 } cout << endl; // 指標重置到陣列的尾端 ptr = numbers + size - 1; // 指標進行遞減操作 cout << "指標遞減存取陣列內容:" << endl; for (int i = 0; i < size; ++i) { cout << *ptr << " "; --ptr; // 指向前一個元素 } cout << endl; return 0; } ``` 輸出結果: ``` 原始陣列: 10 20 30 40 50 指標遞增存取陣列內容: 10 20 30 40 50 指標遞減存取陣列內容: 50 40 30 20 10 ``` ### 指標比較(Comparison of Pointer) --- * 相等比較:用運算子「==」、「!=」 * 關係比較:用運算子「<」、「>」、「<=」、「>=」 以下是有關相等與不相等的比較範例: ```cpp= #include <iostream> using namespace std; int main() { int a = 10, b = 20, c = 10; // 宣告三個指標, 指向這些變數 int* ptr1 = &a; int* ptr2 = &b; int* ptr3 = &c; // 顯示變數的地址 cout << "地址:\n"; cout << "a 的地址: " << &a << endl; cout << "b 的地址: " << &b << endl; cout << "c 的地址: " << &c << endl; // 比較指標相等與不相等 cout << "\n指標比較:\n"; if (ptr1 == ptr2) { cout << "ptr1 和 ptr2 指向相同的地址\n"; } else { cout << "ptr1 和 ptr2 指向不同的地址\n"; } if (ptr1 == ptr3) { cout << "ptr1 和 ptr3 指向相同的地址\n"; } else { cout << "ptr1 和 ptr3 指向不同的地址\n"; } // 修改 ptr3 指向 a ptr3 = &a; cout << "\n修改 ptr3 後的比較:\n"; if (ptr1 == ptr3) { cout << "ptr1 和 ptr3 現在指向相同的地址\n"; } else { cout << "ptr1 和 ptr3 指向不同的地址\n"; } return 0; } ``` 輸出結果: ``` 地址: a 的地址: 0x7ffee1dba88c b 的地址: 0x7ffee1dba88d c 的地址: 0x7ffee1dba88e 指標比較: ptr1 和 ptr2 指向不同的地址 ptr1 和 ptr3 指向不同的地址 修改 ptr3 後的比較: ptr1 和 ptr3 現在指向相同的地址 ``` 以下是有關大小比較的範例: ```cpp= #include <iostream> using namespace std; int main() { int arr[5] = {10, 20, 30, 40, 50}; // 宣告指標, 指向陣列的不同元素 int* ptr1 = &arr[1]; // 指向 arr[1] int* ptr2 = &arr[3]; // 指向 arr[3] // 顯示指標指向的地址與值 cout << "指標資訊:\n"; cout << "ptr1 指向的地址: " << ptr1 << ", 值: " << *ptr1 << endl; cout << "ptr2 指向的地址: " << ptr2 << ", 值: " << *ptr2 << endl; // 比較指標大小 cout << "\n指標比較:\n"; if (ptr1 < ptr2) { cout << "ptr1 指向的地址小於 ptr2 指向的地址\n"; } else if (ptr1 > ptr2) { cout << "ptr1 指向的地址大於 ptr2 指向的地址\n"; } else { cout << "ptr1 和 ptr2 指向相同的地址\n"; } // 顯示指標 <= 和 >= 比較 cout << "\n更多比較:\n"; if (ptr1 <= ptr2) { cout << "ptr1 的地址小於或等於 ptr2 的地址\n"; } if (ptr2 >= ptr1) { cout << "ptr2 的地址大於或等於 ptr1 的地址\n"; } // 進一步操作指標, 改變其指向 ptr1 = &arr[3]; // 將 ptr1 指向 arr[3] cout << "\n修改 ptr1 後的比較:\n"; if (ptr1 == ptr2) { cout << "現在 ptr1 和 ptr2 指向相同的地址\n"; } else { cout << "現在 ptr1 和 ptr2 指向不同的地址\n"; } return 0; } ``` 輸出結果: ``` 指標資訊: ptr1 指向的地址: 0x7ffee6b93010, 值: 20 ptr2 指向的地址: 0x7ffee6b93018, 值: 40 指標比較: ptr1 指向的地址小於 ptr2 指向的地址 更多比較: ptr1 的地址小於或等於 ptr2 的地址 ptr2 的地址大於或等於 ptr1 的地址 修改 ptr1 後的比較: 現在 ptr1 和 ptr2 指向相同的地址 ``` 指標仍有許多相關的應用,本次筆記先至此結束,避免篇幅過長。 總結 --- 本文介紹了 C++ 中的指標(Pointer),包括指標的基本概念、使用方法及相關運算,並探討空指標(Null Pointer)及其應用。 ### 指標的基本概念 --- * 指標(Pointer):指標是一種變數,用於儲存其他變數的記憶體位址。其語法為 `type *ptr;`。 * 記憶體位址獲取:使用 & 符號來獲取變數的記憶體位址。 * 提取運算子(Dereference):* 用於提取指標所指向的值。 ### 空指標(Null Pointer) --- * 定義:其值為 NULL,表示不指向任何有效的記憶體位址。 * 應用: 1. 初始化:將指標初始化為空值來避免未定義行為。 2. 錯誤處理:用於表示資料缺失或錯誤情況。 3. 資源釋放:在釋放資源後將指標設為空值以避免意外使用。 ### 指標算術運算 --- 指標可以進行算術運算,如遞增(++)、遞減(--)、加法和減法,可以方便的遍歷陣列元素。 ### 指標比較 --- 指標可以進行相等比較(==, !=)和關係比較(如 <, >)。 參考資料 --- [C++ 指针的算术运算 | 菜鸟教程](https://www.runoob.com/cplusplus/cpp-pointer-arithmetic.html) [C++ Pointer Arithmetic - GeeksforGeeks](https://www.geeksforgeeks.org/cpp-pointer-arithmetic/) [指標與位址](https://openhome.cc/Gossip/CppGossip/Pointer.html) [C++ Pointers - GeeksforGeeks](https://www.geeksforgeeks.org/cpp-pointers/) [NULL Pointer in C++ - GeeksforGeeks](https://www.geeksforgeeks.org/null-pointer-in-cpp/) [C++ 指针 | 菜鸟教程](https://www.runoob.com/cplusplus/cpp-pointers.html) [C++ Null 指针 | 菜鸟教程](https://www.runoob.com/cplusplus/cpp-null-pointers.html)