在 C 語言 和 C++ 中,數組和指針 有非常密切的關系。它們在某些情況下表現類似,但也有重要的區別。理解數組和指針的關系對于掌握低級內存操作和優化程序性能至關重要。
1. 數組和指針的基本關系
- 數組是一個 連續存儲的元素集合,在內存中占據一段連續的地址空間。
- 數組的名稱(如
arr
)在很多情況下會退化為指向數組第一個元素的指針。
1.1 數組名是一個指針的表現
假設有一個數組:
int arr[5] = {1, 2, 3, 4, 5};
-
數組名的含義:
arr
是一個常量指針,指向數組的第一個元素(&arr[0]
)。arr
的類型是int*
。
-
數組元素的訪問:
arr[i]
等價于*(arr + i)
。- 例如:
printf("%d\n", arr[2]); // 輸出 3 printf("%d\n", *(arr + 2)); // 等價于上面
-
地址的訪問:
arr
是數組第一個元素的地址:&arr[0]
。&arr
是整個數組的地址,類型是int (*)[5]
,和&arr[0]
不同。- 示例:
printf("%p\n", arr); // 輸出數組第一個元素的地址 printf("%p\n", &arr[0]); // 等價于 arr printf("%p\n", &arr); // 輸出整個數組的地址(類型是 int (*)[5])
2. 數組和指針的區別
盡管數組名和指針表現很相似,但它們并不完全相同:
2.1 內存分配
- 數組:
- 數組是一個固定大小的內存塊,在定義時分配內存。
- 例如:
int arr[5]; // 為數組分配了連續的 5 個 int 空間
- 指針:
- 指針是一個變量,它存儲的是地址,可以動態指向不同的位置。
- 例如:
int* ptr; ptr = malloc(5 * sizeof(int)); // 動態分配 5 個 int 空間
2.2 數組名是常量,指針是變量
-
數組名是常量指針,不能重新賦值。
- 示例:
int arr[5]; arr = arr + 1; // 錯誤,數組名不能作為左值
- 示例:
-
指針是變量,可以隨時指向其他地址。
- 示例:
int* ptr; int x = 10, y = 20; ptr = &x; // 指向 x ptr = &y; // 可以改變指向,指向 y
- 示例:
2.3 使用 sizeof 的區別
-
對數組和指針使用
sizeof
的結果不同:- 數組 返回整個數組的大小。
- 指針 返回指針變量本身的大小(通常是 4 或 8 字節,取決于系統架構)。
示例:
int arr[5] = {1, 2, 3, 4, 5}; int* ptr = arr;printf("%zu\n", sizeof(arr)); // 輸出 20(5 個 int 的大小) printf("%zu\n", sizeof(ptr)); // 輸出 8(指針本身的大小,假設是 64 位系統)
2.4 數組和指針的類型
- 數組名和指針的類型不同:
arr
的類型是int[5]
,退化后是int*
。ptr
的類型是int*
,可以自由賦值。
3. 數組和指針在函數中的關系
3.1 數組作為函數參數時會退化為指針
當數組作為函數參數傳遞時,數組會退化為指針傳遞,函數實際上接收到的是指向數組第一個元素的指針。
示例:
void printArray(int arr[], int size)
{for (int i = 0; i < size; i++){printf("%d ", arr[i]); // 等價于 *(arr + i)}
}int main()
{int arr[5] = {1, 2, 3, 4, 5};printArray(arr, 5); // 傳遞的是 int* 指針return 0;
}
實際的函數簽名
上述函數 void printArray(int arr[], int size)
實際等價于:
void printArray(int* arr, int size);
3.2 多維數組的函數參數
多維數組的第一級會退化為指針,但其余維度需要明確指定大小。
示例:
void print2DArray(int arr[][3], int rows)
{for (int i = 0; i < rows; i++){for (int j = 0; j < 3; j++){printf("%d ", arr[i][j]); // 訪問二維數組元素}printf("\n");}
}int main()
{int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};print2DArray(arr, 2); // 傳遞的是 int (*)[3] 指針return 0;
}
注意
- 函數參數中必須指定第二維(
3
),因為編譯器需要知道每行的長度。
4. 結合使用數組和指針的技巧
4.1 動態分配多維數組
可以使用指針動態分配多維數組,而不是直接定義多維數組。
示例:
int** create2DArray(int rows, int cols)
{int** arr = malloc(rows * sizeof(int*)); // 分配行指針for (int i = 0; i < rows; i++){arr[i] = malloc(cols * sizeof(int)); // 分配每行的數據}return arr;
}
4.2 遍歷數組的指針操作
可以使用指針運算遍歷數組,而不使用下標。
示例:
int arr[5] = {1, 2, 3, 4, 5};
int* ptr = arr; // 指向數組第一個元素for (int i = 0; i < 5; i++)
{printf("%d ", *(ptr + i)); // 使用指針訪問元素
}
5. 小結
數組和指針的相似點:
- 數組名可以退化為指向第一個元素的指針。
- 數組元素可以通過下標或指針運算訪問。
數組和指針的區別:
- 數組名是常量指針,指針變量可以自由賦值。
sizeof
操作在數組和指針上有不同的行為。- 數組有固定大小,指針可以動態指向不同的位置。