【C++ 筆記】指標(Pointer)(下) - part 13

很感謝你點進來這篇文章。

你好,我並不是什麼 C++、程式語言的專家,所以本文若有些錯誤麻煩請各位鞭大力一點,我極需各位的指正及指導!!本系列文章的性質主要以詼諧的口吻,一派輕鬆的態度自學程式語言,如果你喜歡,麻煩留言說聲文章讚讚吧!

指標與陣列(Pointer vs. Array)

指標與陣列其實是有相關性的,先從定義上來看:

:::success
陣列的定義:
陣列是一組相同型態的元素,這些元素在記憶體中是連續儲存的。陣列的名稱可以視為指向其首項元素的指標。例如,對於一個整數陣列 int arr[5];,arr 實際上是指向 arr 的指標。
:::

:::info
指標的定義:
指標是一種變數,用來儲存其他變數的位址。指標可以指向任何型態的資料,包括陣列。
:::

指標和陣列在很多情況下是可以互換的。例如,一個指向陣列開頭的指標,可以透過使用指標的算術運算或陣列索引來存取陣列。請看下面的程式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#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;
}

輸出結果:

1
2
3
4
5
6
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 相同的功能,能夠遍歷整個陣列。

然而,指標和陣列並不是完全互換的。例如,請看下面的程式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#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):

1
int x = 0;

右值為 0,(專業術語為:literal constant -> 字面常數),而變數 x 為左值。

左值(lvalue):任何有記憶體位址的變數都是左值,包括不可修改的 const 變數。
右值(rvalue):非左值即右值,通常也是運算式所產生暫時的值。
:::

由於一個陣列名稱對應一個指標常數,只要不改變陣列的值,仍然可以用指標形式的運算式。例如,下面是一個有效的語句,把 var[2] 賦值為 500:

1
*(var + 2) = 500;

指標型陣列(Array of Pointers)

以下是指標陣列的宣告方式:

1
int *ptr[MAX];

以上是一個指標陣列,長度共有 MAX 個,裡面所有的元素皆指向整數型態。

你可能會很疑惑,為何還需要指標陣列呢?原因是他有以下這五大優點:

  1. 靈活的記憶體管理
  2. 簡化多維資料結構的處理
  3. 提升效能
  4. 簡化函數參數傳遞
  5. 實現複雜資料結構

以下是一個範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#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;
}

輸出結果:

1
2
3
4
5
6
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

以下是一個指向字元的指針陣列,用以儲存字串列表的範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#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;
}

輸出結果:

1
2
3
4
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)

我們都知道指標是一個變數,用於儲存另一個變數的記憶體位址。

那麼雙重指標就是一個指標指向另一個指標,那麼所儲存的也就是指標的記憶體位址。

語法是加上兩個星號,如下:

1
int **var;

以下是一個範例:

1
2
3
int value = 100;
int *ptr = &value; // 單層指標,指向整數值
int **doublePtr = &ptr; // 雙層指標,指向單層指標

那他有什麼用呢?請看以下三點:

  1. 動態記憶體管理:雙重指標常用於動態分配二維陣列或其他複雜資料結構。由於需要管理多個指標,使用雙重指標可以方便地處理記憶體分配和釋放。
  2. 函數參數傳遞:當需要在函數中修改指標本身時,雙重指標非常有用。使其可在函數內部改變傳入的指標,指向新的記憶體位址。
  3. 處理複雜資料結構:雙重指標也適用於處理更複雜的資料結構,如鏈結串列、二元樹等。在這些結構中,常需要透過多層的 Dereference 來存取或修改節點。

再來練習一題!以下是一個範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#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;
}

輸出結果:

1
2
3
var 值為 : 3000
*ptr 值為 : 3000
**pptr 值為 : 3000

函數回傳指標

語法:

1
2
3
4
5
6
int * myFunction()
{
.
.
.
}

此函數即表為可以回傳指標的函數。

另外,C++ 不支援函數外回傳局域變數的位址,除非定義局域變數為 static 變數。

以下是一個範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#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;
}

輸出結果:

1
The number is: 42

總結

指標與陣列息息相關,可透過指標對陣列做出一些運算。最常見就是拿指標遍歷一個陣列。

以下是指標陣列的宣告方式:int *ptr[MAX];

雙重指標即加上兩顆星星:int **var;

C++中可以在函數裡面回傳指標。

以上,結束。

參考資料

C++ Pointer To Pointer (Double Pointer) - GeeksforGeeks

C/C++ 中的雙指標

指標的指標

C++ 左值與右值 - HackMD

C++ 指针 vs 数组 | 菜鸟教程

C++ 指针数组 | 菜鸟教程

C++ 指向指针的指针(多级间接寻址) | 菜鸟教程

C++ 从函数返回指针 | 菜鸟教程