在C语言中,指针和数组是密不可分的核心概念。理解它们的关系和操作方式,是掌握C语言的关键。本文将通过一维数组、二维数组和指针数组的实例,详细讲解指针与数组的交互方式,帮助新手彻底掌握这些知识点。
1. 直接访问 vs 间接访问
-
直接访问:通过数组名直接操作元素。
int a[5] = {1, 2, 3, 4, 5}; printf("a[2] = %d\n", a[2]); // 输出3 printf("地址:%p\n", &a[2]); // 输出a+2的地址
-
间接访问:通过指针间接操作元素。
int *p = a; // p指向数组首地址 printf("*(p+2) = %d\n", *(p+2)); // 输出3 printf("地址:%p\n", p+2); // 输出p+2的地址
2. 数组名与指针的异同
-
相同点:
a
和p
都指向数组首地址,a[i]
等价于p[i]
。 -
不同点:
-
a
是地址常量,不可修改(如a++
非法)。 -
p
是指针变量,可修改(如p++
合法)。
-
3. 运算符优先级与结合性
-
示例代码:
int a[5] = {10, 20, 30, 40, 50}; int *p = a; printf("%d\n", *(p++)); // 输出10,p指向a[1] printf("%d\n", *++p); // 输出30,p先自增到a[2]
二、指针与二维数组
1. 二维数组的地址表示
-
数组名
a
:表示第一行的地址(行指针)。 -
行地址降级:在行地址前加
*
,得到列地址。int a[2][3] = {{1,2,3}, {4,5,6}}; printf("a[0][1]的地址:%p\n", *a + 1); // 输出a[0][1]的地址 printf("a[1][0]的地址:%p\n", *(a+1)); // 输出a[1][0]的地址
2. 数组指针(行指针)
-
定义:指向整个数组的指针。
int (*p)[3] = a; // p指向二维数组的第一行 printf("a[1][2]的值:%d\n", p[1][2]); // 输出6
3. 访问二维数组元素的方式
-
元素访问:
a[i][j] == *(*(a+i)+j) == *(a[i]+j)
-
地址访问:
&a[i][j] == *(a+i)+j == a[i]+j
三、指针数组的四大应用场景
1. 存储普通变量地址
int x=10, y=20, z=30;
int *ptrArr[3] = {&x, &y, &z};
printf("y的值:%d\n", *ptrArr[1]); // 输出20
2. 处理二维数组的列地址
int a[2][3] = {1,2,3,4,5,6};
int *p[2] = {a[0], a[1]}; // p存储每行的首地址
printf("a[1][1]的值:%d\n", *(p[1]+1)); // 输出5
3. 管理字符串数组
char *strArr[3] = {"Hello", "World", "优快云"};
printf("第二个字符串:%s\n", strArr[1]); // 输出World
4. 命令行参数
int main(int argc, char *argv[]) {
printf("程序名:%s\n", argv[0]);
return 0;
}
argc
:参数个数。-
argv
:指针数组,存储命令行参数。
四、常见误区与注意事项
-
指针与数组名的区别:
-
数组名是常量,不可修改;指针是变量,可指向其他地址。
-
-
指针运算的步长:
-
数组指针的步长由数组长度决定,例如
int (*p)[3]
的p+1
会跳过3个int
的空间。
-
-
内存管理:
-
若指针数组元素指向动态内存,需手动释放避免内存泄漏。
-
五、实战练习
题目:计算3个学生4门课的总平均分,并输出第n个学生的成绩。
int a[3][4] = {{65,55,23,57}, {52,67,64,80}, {90,42,75,92}};
int (*p)[4] = a;
int sum = 0, n;
for (int i=0; i<3; i++) {
for (int j=0; j<4; j++) {
sum += p[i][j];
}
}
int avg = sum / 12;
printf("平均分:%d\n", avg);
scanf("%d", &n); // 输入学生编号(1~3)
for (int j=0; j<4; j++) {
printf("%d ", p[n-1][j]); // 输出第n个学生的成绩
}