文章目录
0. 前言
📣按照国际惯例,首先声明:本文只是我自己学习的理解,虽然参考了他人的宝贵见解及成果,但是内容可能存在不准确的地方。如果发现文中错误,希望批评指正,共同进步。
在C语言中,指针和数组是密不可分的两个概念。数组名本质上就是一个指向其第一个元素的常量指针,而指针则可以用来遍历数组、操作数组内容,甚至模拟多维数组等。
理解指针与数组之间的关系,对于掌握C语言编程的核心技能至关重要。本文将系统地讲解指针在数组上的各种应用,包括:
- 指针访问数组元素
- 使用指针遍历数组
- 指针与字符串(字符数组)
- 二维数组与指针数组
1. 基本概念回顾
1.1 数组的本质
在C语言中,数组是一块连续的内存区域,用于存储相同类型的数据。
int arr[5] = {1, 2, 3, 4, 5};
此时 arr 是一个包含 5 个整型元素的数组。
1.2 指针的本质
指针是一个变量,它保存的是某个数据对象的地址。
int *p = arr; // p 指向数组 arr 的第一个元素
此时 p == &arr[0],也就是说,p 指向数组的第一个元素。
2. 指针访问数组元素
通过以下指针访问数组元素的示例,可以说明指针与数组下标等价性:
#include<stdio.h>
int main()
{
int arr[] = { 10, 20, 30,40,50 };
int* p = arr;
for (int i = 0; i < 5; i++)
{
printf("arr[%d]=%d\n", i, *(p + i));
}
printf("**************************\n");
for (int i = 0; i < 5; i++)
{
printf("arr[%d]=%d\n", i, arr[i]);
}
return 0;
}
输出结果:

*(p + i)等价于arr[i]。- 这种方式通过指针移动来访问数组元素。
3. 使用指针遍历数组
指针不仅可以访问单个元素,还可以高效地遍历整个数组, 示例如下:
#include<stdio.h>
int main()
{
int arr[] = { 10,20,30,40,50 };
int* p = arr;
while (p < &arr[4])
{
printf("arr[%d]=%d\n",p-&arr[0],*p );
p++;
}
return 0;
}
输出结果:

4. 指针与字符串(字符数组)
在C语言中,字符串本质上是一个字符数组,并以 \0 结尾。
#include<stdio.h>
int main()
{
char str[] = "hello";
char str2[] = { 'w','o','r','l','d' }; //末尾没加'\0',输出乱码
char str3[] = { 'w','o','r','l','d','\0'}; //末尾要加'\0'
printf("%s", str); //%s 需要一个 char* 指针
printf("\n");
printf("%s", str2);
printf("\n");
printf("%s", str3);
}
输出为:

字符串常量(如 "Hello")实际上是一个指向其第一个字符的指针。示例如下:
#include<stdio.h>
int main()
{
const char* str = "hello"; //不写const会报错
char str2[] = "world";
printf("str:%s\n", str);
printf("str2:%s\n", str2);
return 0;
}
输出为:

这里最重要的区别是char *str 必须写成const char *str,表示常量不可修改。

| 类型 | 是否可修改 | 特点 |
|---|---|---|
char *str | ❌ 不可修改 | 指向字符串常量,尝试修改会出错 |
char str[] | ✅ 可修改 | 实际上是一个字符数组 |
通过指针也可以操作字符串,示例如下:
#include<stdio.h>
int main()
{
char str[] = "Programming";
char* p = str;
while (*p != '\0') // '\0'是结尾符
{
printf("%c", *p);
p++;
}
}
输出结果:

5. 多级指针与二维数组
5.1 二维数组
二维数组在内存中是按行优先顺序存储的,也可以用指针访问。示例如下:
#include <stdio.h>
int main() {
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
int (*p)[3] = matrix; // p 是指向含有 3 个整数的数组的指针
for(int i = 0; i < 2; i++) {
for(int j = 0; j < 3; j++) {
printf("matrix[%d][%d] = %d\n", i, j, *(*(p + i) + j));
}
}
return 0;
}
-
p是一个指向包含3个整数的数组的指针(即指向二维数组的一行)。 -
p + i将指针p移动i行(因为p的类型是int(*)[3],所以指针算术会以行为单位)。 -
*(p + i)解引用这个指针,得到第i行的数组(相当于matrix[i])。 -
*(p + i) + j现在是一个指向第i行第j列的指针(因为*(p + i)是一个int[3]数组,它会退化为指向其首元素的指针)。 -
*(*(p + i) + j)最后解引用这个指针,得到第i行第j列的实际整数值(相当于matrix[i][j])。
5.2 指针数组(难点)
有时我们需要处理多个字符串或不同长度的一维数组,这时可以使用指针数组。
#include <stdio.h>
int main() {
// 定义一个指针数组colors,包含3个指向字符串常量的指针
// "red", "green", "blue"是存储在只读内存区的字符串常量
char *colors[] = {"Red", "Green", "Blue"};
// 定义一个二级指针p,指向colors数组的首元素
// 由于colors数组的元素是char*类型,所以p的类型是const char**
char **p = colors;
for(int i = 0; i < 4; i++) {
// *(p + i) 等价于 colors[i],即第i个字符串的地址
printf("colors[%d]: %s\n", i, *(p + i));
}
return 0;
}
输出结果:

关键点解析:
-
const char* colors[]- 这是一个指针数组,每个元素是一个指向字符串常量的指针。
const表示这些字符串不可修改(存储在只读内存区)。
-
const char** p = colorscolors是数组名,(在大多数情况下)会退化为指向首元素的指针(即&colors[0])。- 由于
colors[0]是const char*类型,所以p的类型是const char**(指向const char*的指针)。
-
*(p + i)p + i是指针算术运算,相当于&colors[i]。*(p + i)解引用后得到colors[i],即第i个字符串的地址。
5.3 二维数组与指针数组对比
在 C 语言中,字符串数组还可以通过二维数组的方式进行初始化:
#include <stdio.h>
int main() {
// 方式1:显式指定每个字符串(二维数组)
char colors1[3][6] = { // 每个字符串最长5字符(+1给'\0')
"red",
"green",
"blue"
};
// 方式2:不指定行数,自动推断
char colors2[][6] = {
"red",
"green",
"blue"
};
// 访问示例
printf("%s\n", colors1[0]); // 输出: red
printf("%s\n", colors2[1]); // 输出: green
return 0;
}
特点:
- 每个字符串占用固定长度(如
char colors1[3][6]表示 3 个字符串,每个最多 5 字符 +'\0')。 - 如果字符串长度不同,会浪费空间(如
"red"只占 4 字节,但会占用 6 字节)。
两种方式对比如下:
| 方式 | 示例 | 特点 |
|---|---|---|
| 二维数组 | char colors[3][6] = { "red", "green", "blue" }; | 固定长度,可能浪费空间 |
| 指针数组 | char *colors[] = { "red", "green", "blue" }; | 不可修改,但节省内存 |
推荐:
- 如果字符串不需要修改,用 指针数组(
char *colors[])。 - 如果字符串需要修改,用 二维数组 或动态分配内存。
6. 函数传参中的指针与数组
在C语言中,函数参数传递数组时,实际上传递的是数组的首地址(即指针)。
#include<stdio.h>
void PrintArray(int* arr, int size)
{ for(int i = 0; i < size; i++) {
printf("%d", *(arr + i));
}
printf("\n");
}
int main() {
int arr[] = { 1,2,3,4,5 };
PrintArray(arr, 5);
return 0;
}
输出结果:

- 函数内部无法获取数组长度,必须手动传入。
- 函数形参写成
int arr[]或int *arr都是等价的。
7. 总结
-
指针与数组的等价关系
- 数组名本质是首元素地址的常量指针(
arr ≡ &arr[0]) - 指针运算与数组索引完全等价(
*(p+i) ≡ arr[i])
- 数组名本质是首元素地址的常量指针(
-
多维结构的访问
- 二维数组:
int (*p)[3]指向行 - 指针数组:
char **p处理变长字符串集合
- 二维数组:
-
内存安全三原则
- 严禁越界访问(数组/指针移动必须严格受限)
- 字符串必须NUL终止(
'\0'缺失是万恶之源) - 常量数据加const保护(
const char*防误改)
指针是C语言的灵魂所在,它给予你接近硬件的自由,同时也要求你承担相应的责任。唯有严谨和练习,才能让这份力量为你所用!
385

被折叠的 条评论
为什么被折叠?



