C++ 指標與參照
📖30 分鐘閱讀
#C++#初學#Pointers#指標
本文介紹 C++ 指標與參照的概念,包括指標的宣告與運算、函數指標、陣列與雙重指標、靜態與動態記憶體配置。
目錄9 項
C++ 指標與參照
指標(Pointer)
1. 宣告指標變數
指標用於儲存變數的記憶體位址。
語法:
資料型態 *指標變數;
範例:
int *ptr; // 宣告整數指標
指向變數:
int num = 20;
int *ptr = # // ptr 指向 num 的位址
// 或
int *ptr;
ptr = #
2. 介紹
- 指標儲存位址:指標儲存變數的記憶體位址,
ptr指向變數a表示ptr儲存a的位址。 - 指標自身有位址:指標變數本身也有記憶體位址,與其指向的變數位址無關。
- 重新指向:指標可指向另一同型態變數。
- 固定大小:指標本身佔 4 byte(32 位系統)或 8 byte(64 位系統),與指向的資料型態無關。
範例:指標大小:
#include <iostream>
using namespace std;
int main() {
int *iptr;
char *cptr;
cout << "sizeof(iptr)=" << sizeof(iptr) << endl; // 指標大小
cout << "sizeof(cptr)=" << sizeof(cptr) << endl; // 指標大小
cout << "sizeof(*iptr)=" << sizeof(*iptr) << endl; // 整數大小
cout << "sizeof(*cptr)=" << sizeof(*cptr) << endl; // 字元大小
return 0;
}
圖示:
- 指標指向變數的記憶體位址(待補充圖示)。
- 指標自身的記憶體位址(待補充圖示)。
3. 運算子
- 位址運算子
&:取得變數的記憶體位址。 - 取值運算子
*:取得指標指向變數的內容。
範例:
int num = 20;
int *ptr = # // &num 取得 num 位址
cout << *ptr; // *ptr 取得 num 的值(20)
4. 指標與函數的應用
4.1 傳遞指標到函數
語法:
傳回值型態 函數名稱(資料型態 *指標變數) {
// 函數內容
}
呼叫方式:
int a = 10;
int *ptr = &a;
func(&a); // 傳遞 a 的位址
func(ptr); // 傳遞指標
4.2 回傳指標
語法:
傳回值型態 *函數名稱(資料型態 引數) {
// 函數內容
return 指標;
}
練習:回傳兩數最大值的指標:
#include <iostream>
using namespace std;
int *max(int *p1, int *p2) {
if (*p1 > *p2) {
return p1;
} else {
return p2;
}
}
int main() {
int a = 12, b = 19, *ptr;
ptr = max(&a, &b);
cout << "max=" << *ptr << endl; // 輸出 19
return 0;
}
4.3 傳值與傳址的差異
- 傳值(Pass by Value):傳遞變數副本,函數內修改不影響原變數。
- 傳址(Pass by Address):傳遞變數位址,函數內修改影響原變數。
傳值範例(無效交換):
#include <iostream>
using namespace std;
void swap(int x, int y) {
int tmp = x;
x = y;
y = tmp;
}
int main() {
int a = 5, b = 20;
cout << "交換之前 a=" << a << ", b=" << b << endl;
swap(a, b);
cout << "交換之後 a=" << a << ", b=" << b << endl; // a=5, b=20
return 0;
}
傳址範例(有效交換):
#include <iostream>
using namespace std;
void swap(int *p1, int *p2) {
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
int main() {
int a = 5, b = 20;
cout << "交換之前 a=" << a << ", b=" << b << endl;
swap(&a, &b);
cout << "交換之後 a=" << a << ", b=" << b << endl; // a=20, b=5
return 0;
}
狀況說明(原文件中的 ! 標記):
- 傳值:函數內操作副本,原變數不變。
- 傳址:函數內操作原變數位址,修改生效。
- 注意:傳址需小心,避免意外修改或空指標(null pointer)問題。
- 指標操作需確保指向有效記憶體。
5. 函數指標(Function Pointer)
函數名稱儲存函數的起始位址,可用函數指標指向並呼叫。
語法:
傳回值型態 (*指標變數名稱)(引數型態1, 引數型態2, ...);
範例:
#include <iostream>
using namespace std;
int square(int n) {
return n * n;
}
int main() {
int (*pf)(int);
pf = square;
cout << "square(5)=" << (*pf)(5) << endl; // 輸出 25
return 0;
}
傳遞函數指標:
#include <iostream>
using namespace std;
double triangle(double base, double height) {
return base * height / 2;
}
void show_area(double x, double y, double (*pf)(double, double)) {
cout << "Area=" << (*pf)(x, y) << endl;
}
int main() {
show_area(4, 6.6, triangle); // 輸出 Area=13.2
return 0;
}
6. 指標與陣列的關係
6.1 指標的算術運算
- 指標可進行加減運算,移動單位為指向資料型態的大小(byte)。
- 陣列名稱是指標常數,指向陣列首元素。
範例:存取陣列:
#include <iostream>
using namespace std;
int main() {
int a[3] = {5, 7, 9};
cout << "a[0]=" << a[0] << ", *(a+0)=" << *(a+0) << endl;
cout << "a[1]=" << a[1] << ", *(a+1)=" << *(a+1) << endl;
cout << "a[2]=" << a[2] << ", *(a+2)=" << *(a+2) << endl;
return 0;
}
練習:計算陣列總和:
方法一(使用陣列名稱):
#include <iostream> using namespace std; int main() { int a[5] = {1, 2, 3, 4, 5}, sum = 0; for (int i = 0; i < 5; i++) { sum += *(a + i); } cout << "Sum=" << sum << endl; // 輸出 15 return 0; }方法二(使用指標):
#include <iostream> using namespace std; int main() { int a[5] = {1, 2, 3, 4, 5}, sum = 0; int *ptr = a; for (int i = 0; i < 5; i++) { sum += *(ptr++); } cout << "Sum=" << sum << endl; // 輸出 15 return 0; }
注意:
- 陣列名稱(如
a)是指標常數,不可修改(a = a + 1無效)。 - 指標變數(如
ptr)可修改(ptr = ptr + 1有效)。
練習:指標常數的位址與值:
#include <iostream>
using namespace std;
int main() {
int a[5] = {32, 16, 35, 65, 52};
cout << "a=" << a << endl; // 陣列首地址
cout << "&a=" << &a << endl; // 陣列本身的地址
for (int i = 0; i < 5; i++) {
cout << "&a[" << i << "]=" << &a[i] << endl; // 各元素地址
}
return 0;
}
傳遞陣列到函數:
#include <iostream>
using namespace std;
void func(int *arr, int n) {
for (int i = 0; i < n; i++) {
cout << arr[i] << ' ';
}
cout << endl;
}
int main() {
int arr[3] = {1, 2, 3};
func(arr, 3); // 傳遞陣列首地址
return 0;
}
6.2 指標陣列
儲存指標的陣列,常用於字串陣列。
語法:
資料型態 *陣列名稱[元素個數];
範例:
#include <iostream>
using namespace std;
int main() {
int x = 1, y = 2, z = 3;
int *ptr[3] = {&x, &y, &z};
for (int i = 0; i < 3; i++) {
cout << *ptr[i] << ' ';
}
cout << endl;
return 0;
}
6.3 指標指向字串
字串可用字元陣列或字元指標表示。
範例:
#include <iostream>
using namespace std;
int main() {
char name[20];
char *ptr = "how are you?";
cout << "what's your name?" << endl;
cin.getline(name, 20);
cout << "hi, " << name << ", " << ptr << endl;
return 0;
}
字串陣列範例:
#include <iostream>
using namespace std;
int main() {
char *ptr[3] = {"apple", "banana", "cherry"};
for (int i = 0; i < 3; i++) {
cout << ptr[i] << endl;
}
return 0;
}
6.4 雙重指標(Pointer to Pointer)
指向指標的指標,儲存另一指標的位址。
語法:
資料型態 **雙重指標;
資料型態 *(*雙重指標); // 可加括號
二維陣列與雙重指標:
- 二維陣列名稱可視為指向一維陣列的指標。
*(num + m) + n:第 m+1 列,第 n+1 行的位址。*(*(num + m) + n):第 m+1 列,第 n+1 行的值。
練習:印出二維陣列位址與值: {% raw %}
#include <iostream>
using namespace std;
int main() {
int num[3][4] = {{12, 23, 42, 18}, {43, 22, 16, 14}, {31, 13, 19, 28}};
for (int m = 0; m < 3; m++) {
for (int n = 0; n < 4; n++) {
cout << "num[" << m << "][" << n << "]=" << *(*(num + m) + n);
cout << ", 地址=" << *(num + m) + n << endl;
}
}
return 0;
}
{% endraw %}
圖示:
- 二維陣列與指標陣列的關係(待補充圖示)。
- 雙重指標存取二維陣列的示意(待補充圖示)。
7. 靜態與動態記憶體配置
7.1 靜態記憶體配置
- 定義:編譯時分配固定記憶體,程式執行時使用。
- 特點:記憶體大小固定,生命週期隨程式執行。
圖示:靜態記憶體配置示意(待補充圖示)。
7.2 動態記憶體配置
- 定義:執行時從堆(heap)分配記憶體。
- 特點:靈活分配,需手動釋放。
指標創建與釋放:
型態A *指標變數名稱;
指標變數名稱 = new 型態A;
delete 指標變數名稱; // 釋放記憶體
範例:動態配置整數:
#include <iostream>
using namespace std;
int main() {
int *a = new int; // 分配整數記憶體
*a = 5;
cout << "*a=" << *a << endl; // 輸出 5
delete a; // 釋放記憶體
a = nullptr; // 避免野指標
// cout << "*a=" << *a << endl; // 錯誤:訪問已釋放記憶體
return 0;
}
陣列創建與釋放:
型態A *指標變數 = new 型態A[個數];
delete[] 指標變數; // 釋放陣列記憶體
指標變數 = nullptr;
練習:動態配置整數陣列:
#include <iostream>
using namespace std;
int main() {
int *a = new int[5];
for (int i = 0; i < 5; i++) {
a[i] = i * 2;
cout << "a[" << i << "]=" << a[i] << " ";
}
cout << endl;
delete[] a;
a = nullptr;
return 0;
}
範例:動態配置字串:
#include <iostream>
#include <cstring>
using namespace std;
char *setString(const char *text) {
char *ptr = new char[strlen(text) + 1];
strcpy(ptr, text);
return ptr;
}
int main() {
char *str = setString("hello c++");
cout << str << endl;
delete[] str;
str = nullptr;
return 0;
}
8. 指標與參照
8.1 指標
使用 &(位址運算子)和 *(取值運算子)操作變數位址與內容。
8.2 參照
參照是變數的別名,直接操作原變數。
語法:
資料型態 &參照名稱 = 變數名稱;
範例:
#include <iostream>
using namespace std;
int main() {
int x = 5;
int &ref = x; // ref 是 x 的別名
cout << "x=" << x << ", ref=" << ref << endl; // x=5, ref=5
ref = 10; // 修改 ref,等同修改 x
cout << "x=" << x << ", ref=" << ref << endl; // x=10, ref=10
return 0;
}
練習:指標與參照結合:
#include <iostream>
using namespace std;
int main() {
int a = 10, &ref = a;
int b = 15, *ptr = &b;
cout << a << "+" << b << "=" << ref + *ptr << endl; // 輸出 25
return 0;
}
8.3 引數傳遞方式
- 傳值(Pass by Value):傳遞副本,函數內修改不影響原變數。
- 傳址(Pass by Address):傳遞位址,函數內修改影響原變數。
- 傳參照(Pass by Reference):傳遞別名,函數內修改影響原變數。
傳參照範例:
#include <iostream>
using namespace std;
void swap(int &x, int &y) {
int tmp = x;
x = y;
y = tmp;
}
int main() {
int a = 5, b = 20;
cout << "交換之前 a=" << a << ", b=" << b << endl;
swap(a, b);
cout << "交換之後 a=" << a << ", b=" << b << endl; // a=20, b=5
return 0;
}
圖示:傳值、傳址、傳參照的比較(待補充圖示)。