一、定义与本质区别
- 指针(Pointer)
定义:变量类型,存储内存地址值,可指向任意类型的数据或函数。
本质:独立变量,拥有自己的内存空间(如32位系统占4字节)。
示例:
int *p; // 指针变量,未初始化时指向随机地址
- 数组(Array)
定义:连续内存块的别名,存储同类型元素的集合。
本质:符号常量(如a代表数组首元素地址),无独立存储空间。
示例:
int arr[5](); // 数组名arr不可修改,代表首元素地址
- 核心关系
- 数组名的本质:
- 数组名是首元素地址的常量指针,例如 int arr[5] 中,arr 等价于 &arr[0],但其地址不可修改(如 arr++ 非法)。
- 例外:sizeof(arr) 返回整个数组的字节大小,而非指针大小;&arr 的类型为 int(*)[5]((指向整个数组的指针)。
- 指针的灵活性:
- 指针是变量,可指向不同地址(如 int* p = arr; p++ 合法)。
- 数组元素访问 arr[i] 会被编译器转换为 *(arr + i),与指针操作一致。
- 数组名的本质:
二、内存分配与操作特性
典型错误示例:
int arr = {1,2,3,4,5};
int* p = arr;
// arr++; // 错误!数组名为常量指针
p++; // 正确,指针可移动
三、高级类型:指针数组 vs 数组指针
-
指针数组(元素为指针)
int* ptr_arr; // 含3个int*元素的数组 int a=1, b=2, c=3; ptr_arr = &a; // 存储变量地址
-
数组指针(指向数组的指针)
int (*arr_ptr); // 指向含5个int元素的数组 int matrix; arr_ptr = matrix; // 指向二维数组的行
关键区别:
int* p
:优先结合[]
,定义指针数组。int (*p)
:优先结合*
,定义数组指针。
四、函数参数传递的交互
-
数组退化为指针
函数参数中的数组声明等价于指针:void func(int arr[]); // 实际为 int* arr void func(int* arr); // 等效声明
- 需额外传递数组长度(如
int size
)。
- 需额外传递数组长度(如
-
多维数组传递
需明确第二维长度:void func(int (*mat) = {10, 20, 30}; // 内存布局:(连续地址)
-
指针管理动态数组
int* p = new int; // 堆分配 p = 42; // 等价于 *(p+2) delete[] p; // 必须手动释放
六、典型应用场景
-
字符串处理
char str[]
与char*
混用,但需注意缓冲区溢出。
-
函数回调
void sort(int* arr, int size, bool (*compare)(int, int));
-
硬件/系统编程
- 直接操作内存映射(如
volatile uint32_t* reg = 0x40000000;
)。
- 直接操作内存映射(如
总结
- 核心原则:数组提供结构化存储,指针提供灵活访问,二者通过地址计算紧密关联。
- 避坑指南:
- 避免越界访问(使用
std::vector
或智能指针优化安全性)。 - 区分指针算术步长(如
int*
步长为4字节,double*
为8字节)。 - 多维数组操作时注意类型降级(如
int[][5]
退化为int(*)
)。
- 避免越界访问(使用
示例
一维数组的指针操作
-
基础遍历法
int arr[] = {1,2,3,4,5}; int *p = arr; // 指针指向数组首地址 for(int i=0; i<5; i++) { printf("%d ", *(p+i)); // 通过指针偏移访问元素 }
- 等效操作:
p[i]
或*(arr+i)
,三者底层均转换为地址计算
- 等效操作:
-
指针自增法
int *end = arr + 5; // 计算结束地址 for(int *p=arr; p<end; p++) { printf("%d ", *p); // 直接操作指针移动 }
- 注意:指针自增步长由数据类型决定(如
int*
每次移动4字节)
- 注意:指针自增步长由数据类型决定(如
多维数组的指针操作
-
二维数组打印
int arr = {{1,2,3}, {4,5,6}}; int (*p) = arr; // 数组指针,指向含3个元素的数组 for(int i=0; i<2; i++) { for(int j=0; j<3; j++) { printf("%d ", *(*(p+i)+j)); // 解引用两次 } }
-
指针数组方案
int *ptr_arr = {arr, arr }; // 存储行指针 for(int i=0; i<2; i++) { for(int j=0; j<3; j++) { printf("%d ", ptr_arr[i][j]); // 类似二维数组访问 } }
动态数组的指针操作
-
malloc分配数组
int *dyn_arr = (int*)malloc(5 * sizeof(int)); for(int i=0; i<5; i++) { dyn_arr[i] = i+1; // 指针下标法赋值 printf("%d ", *(dyn_arr+i)); } free(dyn_arr);
-
智能指针方案(C++)
#include <memory> auto sp = std::make_unique<int[]>(5); for(int i=0; i<5; i++) { sp[i] = i+1; std::cout << *(sp.get()+i); // 通过get()获取原始指针 }
特殊类型数组处理
-
字符串数组打印
const char *strs[] = {"Hello", "World"}; for(int i=0; i<2; i++) { printf("%s\n", strs[i]); // 直接输出字符串指针 }
-
结构体数组访问
typedef struct { int x,y; } Point; Point points = {{1,2}, {3,4}, {5,6}}; Point *p = points; for(int i=0; i<3; i++) { printf("(%d,%d) ", (p+i)->x, (p+i)->y); // 指针访问成员 }
调试与安全建议
-
地址验证
使用printf("%p", p)
打印指针地址,确认操作范围 -
边界防护
int *end = arr + size; while(p < end) { // 避免越界访问 // 操作代码 }
-
Valgrind检测
对动态分配内存进行泄漏检测:valgrind --leak-check=full ./a.out