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