29-c语言指针和数组
在C语言中,指针是存储内存地址的变量,可修改指向;数组是连续存储相同类型数据的集合,其数组名在表达式中会退化为指向首元素的常量指针(如
arr等价于&arr[0]),因此数组访问(如arr[i])与指针操作(如*(arr+i))等价。关键区别在于:指针数组(如int *p[3])是元素为指针的数组,而数组指针(如int (*p)[3])是指向整个数组的指针,混淆二者易导致错误。需注意指针不可未初始化、数组不可越界,且数组名作为常量不可修改(如arr++非法),这些是正确使用指针与数组的核心要点。
一、基本概念
1. 指针
- 定义:指针是存储内存地址的变量,可以指向任何类型的数据。
- 特点:
- 指针是变量,可以修改指针的值(即改变它指向的内存地址)
- 指针有类型,决定了它指向的数据类型和移动时的"步长"(如int*指针移动1位,地址增加4字节)
- 声明:
类型 *指针名;例如:int *p;
2. 数组
- 定义:数组是一组相同类型数据的有序集合。
- 特点:
- 内存上连续存储:数组元素在内存中占据一块连续的空间
- 数组名是常量:数组名代表数组首元素的地址,但不能被修改(不能给数组名赋值)
- 声明:
类型 数组名[大小];例如:int arr[5];
二、指针与数组的关系
1. 核心联系
- 数组名的本质:数组名在大多数情况下会被隐式转换为指向数组首元素的指针(常量指针)。
int arr[5] = {1,2,3,4,5};int *p = arr;等价于int *p = &arr[0];
- 访问方式等价:
arr[i]等价于*(arr + i)p[i]等价于*(p + i)
2. 函数参数传递
- 当数组作为函数参数传递时,会退化为指向首元素的指针:
void func(int a[])与void func(int *a)完全等价- 函数内部无法通过
sizeof(a)获取数组长度(只能得到指针大小)
三、指针数组与数组指针的区别
| 特性 | 指针数组 | 数组指针 |
|---|---|---|
| 本质 | 数组 | 指针 |
| 定义 | 类型 *数组名[大小]; | 类型 (*指针名)[大小]; |
| 示例 | int *ptr[3]; | int (*p)[3]; |
| 含义 | 一个包含3个int指针的数组 | 一个指向包含3个int元素的数组的指针 |
| 访问方式 | ptr[i] | (*p)[i] |
关键区别:
- 指针数组的元素是独立的指针变量
- 数组指针是指向整个数组的指针
四、常见应用场景
1. 通过指针遍历数组
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;
for (int i = 0; i < 5; i++) {
printf("%d ", *(p + i)); // 或 p[i]
}
2. 指针数组存储多个字符串
char *names[3] = {"Alice", "Bob", "Charlie"};
for (int i = 0; i < 3; i++) {
printf("%s\n", names[i]);
}
3. 指针数组存储函数指针
void func1() { printf("Func1\n"); }
void func2() { printf("Func2\n"); }
void (*funPtr[2])(void) = {func1, func2};
for (int i = 0; i < 2; i++) {
funPtr[i](); // 调用函数
}
4. 数组指针处理二维数组
int arr[3][5] = {
{1, 2, 3, 4, 5},
{6, 7, 8, 9, 10},
{11, 12, 13, 14, 15}
};
int (*p)[5] = arr; // p指向包含5个int的一维数组
printf("%d\n", *((*p) + 1)); // 访问arr[0][1] = 2
printf("%d\n", *((*(p + 1)) + 2)); // 访问arr[1][2] = 8
五、常见错误与注意事项
1. 指针与数组的主要区别
| 特性 | 指针 | 数组 |
|---|---|---|
| 存储内容 | 存储一个地址 | 存储一组同类型数据 |
| 可修改性 | 指针本身的值可修改 | 数组名是常量,不可修改 |
| sizeof结果 | 指针大小(32位系统4字节) | 数组总字节数 |
| 内存分配 | 需手动指向已分配的内存 | 定义时自动分配连续内存块 |
2. 常见错误
-
错误1:访问未初始化的指针
int *p; printf("%d", *p); // 未初始化的指针,导致未定义行为 -
错误2:数组越界
int arr[5]; printf("%d", arr[5]); // 越界访问 -
错误3:空指针解引用
int *p = NULL; printf("%d", *p); // 空指针解引用,导致程序崩溃 -
错误4:混淆指针数组与数组指针
int *p[3]; // 指针数组 int (*p)[3]; // 数组指针
六、深入理解
1. 为什么"数组名是常量指针"?
- 数组名代表数组首元素的地址,但这个地址是常量,不能被修改
- 例如:
arr++会编译错误,因为数组名是常量
2. 指针与数组的"等价性"
- 数组名在表达式中会被转换为指向首元素的指针
- 但数组名本身不是指针,而是一个常量地址
3. 指针数组与二维数组的区别
-
指针数组:每个元素是一个指针,指向独立的内存区域,不一定连续
char *str[3] = {"hello", "world", "c"}; -
二维数组:所有元素在内存中连续存储
char str[3][6] = {"hello", "world", "c"};
七、总结
C语言中指针和数组是紧密关联但又本质不同的概念:
- 指针是变量,可以修改指向的地址
- 数组是连续内存块,数组名是常量地址
- 指针数组是数组,每个元素是指针
- 数组指针是指针,指向一个数组
理解指针与数组的关系是掌握C语言的关键,特别是在处理字符串、多维数组和动态内存管理时。正确区分指针数组和数组指针,避免常见的内存错误,是编写高效、安全C程序的基础。
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 软件从业者Hort
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果

