【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)