C 語(yǔ)言指針與數(shù)組詳解
一、指針的算術(shù)運(yùn)算
1.1 指針加減法原理
指針加減法不是簡(jiǎn)單的數(shù)值加減,而是以數(shù)據(jù)類(lèi)型大小為步長(zhǎng)進(jìn)行移動(dòng)。
#include <stdio.h>
int main() {
int arr[4] = {1, 2, 3, 4};
int* p = arr; // p 指向數(shù)組首元素
printf("初始位置: %d\n", *p); // 輸出: 1
p += 1; // 后移一個(gè)元素(實(shí)際移動(dòng) sizeof(int) 字節(jié))
printf("后移一個(gè)元素: %d\n", *p); // 輸出: 2
p -= 1; // 前移一個(gè)元素
printf("前移一個(gè)元素: %d\n", *p); // 輸出: 1
return 0;
}
1.2 指針運(yùn)算的內(nèi)存布局
內(nèi)存地址: 1000 1004 1008 1012
數(shù)組元素: [ 1 ] [ 2 ] [ 3 ] [ 4 ]
指針位置: p p+1 p+2 p+3
重要:
p + n實(shí)際移動(dòng)n * sizeof(數(shù)據(jù)類(lèi)型)字節(jié)
二、指針與數(shù)組的關(guān)系
2.1 數(shù)組名的本質(zhì)
#include <stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5};
// 數(shù)組名就是首元素地址
printf("arr = %p\n", arr); // 數(shù)組名地址
printf("&arr[0] = %p\n", &arr[0]); // 首元素地址
printf("兩者相等: %s\n", arr == &arr[0] ? "是" : "否");
// ? 錯(cuò)誤:數(shù)組名是常量,不能修改
// arr = 10; // 編譯錯(cuò)誤
// arr = &some_var; // 編譯錯(cuò)誤
return 0;
}
2.2 指針操作數(shù)組的多種方式
#include <stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5};
int *p = arr; // p 指向數(shù)組首元素
printf("指針p的地址: %p\n", p);
printf("數(shù)組arr的地址: %p\n", arr);
// 訪(fǎng)問(wèn)數(shù)組元素的等價(jià)方式
p = arr + 3; // 指向第4個(gè)元素
p = &arr[3]; // 同上,指向第4個(gè)元素
// 修改數(shù)組元素的等價(jià)方式
arr[3] = 10; // 傳統(tǒng)數(shù)組下標(biāo)方式
*(arr + 3) = 10; // 指針?biāo)阈g(shù)方式
p = arr + 3;
*p = 10; // 通過(guò)指針變量方式
return 0;
}
2.3 數(shù)組遍歷的多種方法
#include <stdio.h>
int main() {
int arr[] = {1, 2, 3, 4, 5};
int length = sizeof(arr) / sizeof(arr[0]);
printf("方法1: 傳統(tǒng)下標(biāo)遍歷\n");
for(int i = 0; i < length; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
printf("\n方法2: 指針遍歷\n");
for(int* p = arr; p < arr + length; p++) {
printf("元素: %d (地址: %p)\n", *p, p);
}
printf("\n方法3: 指針?biāo)阈g(shù)遍歷\n");
for(int i = 0; i < length; i++) {
printf("*(arr + %d) = %d\n", i, *(arr + i));
}
return 0;
}
2.4 ?? 數(shù)組越界警告
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;
// ? 危險(xiǎn):數(shù)組越界訪(fǎng)問(wèn)
// *(p + 5) = 99; // 訪(fǎng)問(wèn)第6個(gè)元素,但數(shù)組只有5個(gè)
// ? 危險(xiǎn):負(fù)索引
// *(p - 1) = 99; // 訪(fǎng)問(wèn)數(shù)組之前的內(nèi)存
// ? 安全:始終檢查邊界
int index = 5;
if (index >= 0 && index < 5) {
*(p + index) = 99;
} else {
printf("錯(cuò)誤:數(shù)組索引 %d 越界!\n", index);
}
return 0;
}
三、指針數(shù)組
3.1 什么是指針數(shù)組?
指針數(shù)組是一個(gè)數(shù)組,其中的每個(gè)元素都是指針類(lèi)型。
#include <stdio.h>
int main() {
// 定義三個(gè)整型變量
int a1 = 10, a2 = 20, a3 = 30;
// 定義指針數(shù)組 - 包含3個(gè)int*類(lèi)型的元素
int *pointerArray[3];
// 為指針數(shù)組元素賦值(存儲(chǔ)變量的地址)
pointerArray[0] = &a1; // 存儲(chǔ)a1的地址
pointerArray[1] = &a2; // 存儲(chǔ)a2的地址
pointerArray[2] = &a3; // 存儲(chǔ)a3的地址
return 0;
}
3.2 指針數(shù)組的初始化與使用
#include <stdio.h>
int main() {
int a1 = 10, a2 = 20, a3 = 30;
// 方法1: 定義時(shí)直接初始化
int *pointerArray[] = {&a1, &a2, &a3};
// 計(jì)算數(shù)組元素個(gè)數(shù)
int count = sizeof(pointerArray) / sizeof(pointerArray[0]);
printf("指針數(shù)組遍歷:\n");
for (int i = 0; i < count; i++) {
printf("pointerArray[%d] = %p, *pointerArray[%d] = %d\n",
i, pointerArray[i], i, *pointerArray[i]);
}
return 0;
}
3.3 指針數(shù)組的實(shí)用示例
#include <stdio.h>
int main() {
int x = 100, y = 200, z = 300;
// 指針數(shù)組存儲(chǔ)不同變量的地址
int *numbers[] = {&x, &y, &z};
int count = sizeof(numbers) / sizeof(numbers[0]);
// 通過(guò)指針數(shù)組修改變量值
printf("修改前的值:\n");
for (int i = 0; i < count; i++) {
printf("第%d個(gè)值: %d\n", i, *numbers[i]);
}
// 通過(guò)指針數(shù)組修改所有值
for (int i = 0; i < count; i++) {
*numbers[i] *= 2; // 每個(gè)值乘以2
}
printf("\n修改后的值:\n");
for (int i = 0; i < count; i++) {
printf("第%d個(gè)值: %d\n", i, *numbers[i]);
}
return 0;
}
3.4 字符串指針數(shù)組(常用場(chǎng)景)
#include <stdio.h>
int main() {
// 字符串指針數(shù)組 - 常用于命令行參數(shù)處理
const char *fruits[] = {
"Apple",
"Banana",
"Cherry",
"Date"
};
int count = sizeof(fruits) / sizeof(fruits[0]);
printf("水果列表:\n");
for (int i = 0; i < count; i++) {
printf("%d. %s\n", i + 1, fruits[i]);
}
return 0;
}
四、關(guān)鍵概念總結(jié)
4.1 指針與數(shù)組的區(qū)別
| 特性 | 數(shù)組 | 指針 |
|---|---|---|
| 類(lèi)型 | 數(shù)據(jù)集合 | 地址變量 |
| 內(nèi)存 | 連續(xù)分配 | 存儲(chǔ)地址值 |
| 賦值 | 元素逐個(gè)賦值 | 直接地址賦值 |
| 修改 | 數(shù)組名不可修改 | 指針值可修改 |
4.2 安全使用指南
? 推薦做法:
// 1. 使用sizeof計(jì)算數(shù)組長(zhǎng)度
int arr[] = {1, 2, 3, 4, 5};
int length = sizeof(arr) / sizeof(arr[0]);
// 2. 邊界檢查
if (index >= 0 && index < length) {
// 安全訪(fǎng)問(wèn)
}
// 3. 指針遍歷時(shí)明確終止條件
for (int *p = arr; p < arr + length; p++) {
// 安全遍歷
}
? 避免做法:
// 1. 數(shù)組越界
arr[10] = 5; // 危險(xiǎn)!
// 2. 未初始化的指針訪(fǎng)問(wèn)
int *p;
*p = 10; // 危險(xiǎn)!
// 3. 錯(cuò)誤的指針運(yùn)算
p = arr;
p += 100; // 危險(xiǎn)!
4.3 實(shí)用技巧
// 計(jì)算數(shù)組長(zhǎng)度的宏
#define ARRAY_LENGTH(arr) (sizeof(arr) / sizeof((arr)[0]))
// 安全訪(fǎng)問(wèn)宏
#define SAFE_ACCESS(arr, idx) \
((idx) >= 0 && (idx) < ARRAY_LENGTH(arr) ? (arr)[(idx)] : 0)
int main() {
int numbers[] = {1, 2, 3, 4, 5};
// 安全使用
for (int i = 0; i < ARRAY_LENGTH(numbers); i++) {
printf("%d ", SAFE_ACCESS(numbers, i));
}
return 0;
}
掌握指針與數(shù)組的關(guān)系是C語(yǔ)言編程的核心基礎(chǔ),正確使用它們可以編寫(xiě)出高效且安全的代碼!