C 语言指针与数组深度关联及实战应用
C 语言中数组名本质是指向首元素的常量指针,下标访问等价于指针偏移运算。通过指针算术可高效遍历、求和及逆序数组。需区分指针数组(存指针)与数组指针(指整体),并避免越界访问。掌握二者关系对底层开发至关重要。

C 语言中数组名本质是指向首元素的常量指针,下标访问等价于指针偏移运算。通过指针算术可高效遍历、求和及逆序数组。需区分指针数组(存指针)与数组指针(指整体),并避免越界访问。掌握二者关系对底层开发至关重要。

💡 学习目标:掌握指针与数组的内在联系,熟练运用指针操作数组元素,解决实际开发中的数组遍历、数据交换等问题;学习重点:数组名的本质、指针算术运算操作数组、指针数组与数组指针的区别及应用。
在 C 语言中,数组和指针有着密不可分的联系。很多初学者会混淆数组名和指针变量的概念,其实二者既有关联,又有本质区别。
💡 数组名在大多数情况下会被编译器隐式转换为指向数组首元素的常量指针。
我们来看一段简单的代码:
#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
printf("数组首元素地址:%p\n", arr);
printf("数组首元素地址:%p\n", &arr[0]);
printf("数组首元素值:%d\n", *arr);
return 0;
}
运行结果:
数组首元素地址:0x7ffeefbff560 数组首元素地址:0x7ffeefbff560 数组首元素值:10
从结果可以看出,arr 和 &arr[0] 表示的是同一个地址,都是数组首元素的地址。
使用 *arr 可以直接访问数组的首元素,这就验证了数组名可以被当作指针使用。
⚠️ 注意:数组名是常量指针,不能被修改。
以下代码是错误的:
int arr[5];
arr++; // 错误,数组名是常量,不能进行自增操作
💡 C 语言规定,数组下标访问 arr[i] 等价于指针访问 *(arr + i)。
这个等价关系是指针操作数组的核心,我们可以通过指针算术运算来访问数组的任意元素。
示例代码:
#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int i;
for (i = 0; i < 5; i++) {
printf("arr[%d] = %d, *(arr + %d) = %d\n", i, arr[i], i, *(arr + i));
}
return 0;
}
运行结果:
arr[0] = 10, *(arr + 0) = 10
arr[1] = 20, *(arr + 1) = 20
arr[2] = 30, *(arr + 2) = 30
arr[3] = 40, *(arr + 3) = 40
arr[4] = 50, *(arr + 4) = 50
可以看到,两种访问方式的结果完全一致。
指针算术运算 arr + i 会根据数组元素的类型自动计算偏移量,int 类型的元素每个占 4 字节,arr + 1 就会偏移 4 字节,指向数组的下一个元素。
掌握了数组和指针的关系后,我们就可以用指针来实现各种数组操作,让代码更简洁高效。
🔧 需求:定义一个整型数组,使用指针遍历数组所有元素,计算元素总和。
#include <stdio.h>
int main() {
int arr[6] = {1, 2, 3, 4, 5, 6};
int sum = 0;
int *p = arr; // 指针 p 指向数组首元素
int len = sizeof(arr) / sizeof(arr[0]); // 计算数组长度
// 使用指针遍历数组
for (int i = 0; i < len; i++) {
sum += *p;
p++; // 指针自增,指向下一个元素
}
printf("数组元素总和:%d\n", sum);
return 0;
}
运行结果:
数组元素总和:21
💡 技巧:sizeof(arr) / sizeof(arr[0]) 是计算数组长度的通用方法,适用于任意类型的数组。
🔧 需求:定义一个整型数组,使用指针将数组元素逆序存放,要求不使用额外数组空间。
#include <stdio.h>
void reverseArray(int *arr, int len) {
int *left = arr; // 指向数组首元素
int *right = arr + len - 1; // 指向数组尾元素
int temp;
while (left < right) {
// 交换两个指针指向的元素
temp = *left;
*left = *right;
*right = temp;
left++; // 左指针右移
right--; // 右指针左移
}
}
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int len = sizeof(arr) / sizeof(arr[0]);
int *p = arr;
printf("原数组:");
for (int i = 0; i < len; i++) {
printf("%d ", *(p + i));
}
printf("\n");
reverseArray(arr, len);
printf("逆序数组:");
for (int i = 0; i < len; i++) {
printf("%d ", *(p + i));
}
printf("\n");
return 0;
}
运行结果:
原数组:10 20 30 40 50
逆序数组:50 40 30 20 10
⚠️ 注意:函数参数中的数组名会被直接当作指针处理,在函数内部使用 sizeof(arr) 得到的是指针的大小,而不是数组的实际大小。
在实际开发中,指针数组和数组指针是两个容易混淆的概念,二者的用途截然不同。
💡 指针数组是存放指针的数组,数组的每个元素都是一个指针变量。
定义格式:类型 *数组名 [数组长度];
示例:定义一个指针数组存放 3 个字符串的地址
#include <stdio.h>
int main() {
char *strArr[3] = {"C 语言", "指针", "数组"}; // 指针数组
for (int i = 0; i < 3; i++) {
printf("%s\n", strArr[i]);
}
return 0;
}
运行结果:
C 语言
指针
数组
指针数组的核心是数组,数组的每个元素都是指针类型,常用于存放多个字符串或者多个一维数组的地址。
💡 数组指针是指向整个数组的指针,它指向的是一个数组整体,而不是单个元素。
定义格式:类型 (*指针名)[数组长度];
示例:使用数组指针遍历二维数组
#include <stdio.h>
int main() {
int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*p)[3] = arr; // 数组指针,指向包含 3 个 int 元素的数组
// 遍历二维数组
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", *(*(p + i) + j));
}
printf("\n");
}
return 0;
}
运行结果:
1 2 3
4 5 6
⚠️ 注意:数组指针的定义中,(*p) 的括号不能省略。
如果省略括号,int *p[3] 就变成了指针数组,二者的含义完全不同。
❌ 错误代码:
int arr[3] = {1, 2, 3};
int *p = arr;
for (int i = 0; i < 5; i++) {
printf("%d\n", *p++); // 越界访问,会读取到随机值
}
✅ 解决方案:
sizeof 计算数组长度,确保遍历范围准确。❌ 错误原因:定义时缺少括号,或者对二者的指向对象理解不清。
✅ 解决方案:
类型 *数组名 [],数组指针是 类型 (*指针名) []。✅ 数组名在大多数情况下会被转换为指向首元素的常量指针,arr[i] 等价于 *(arr + i)。
✅ 可以通过指针算术运算高效操作数组,实现遍历、求和、逆序等功能。
✅ 指针数组是存放指针的数组,数组指针是指向整个数组的指针,二者定义和用途不同。
✅ 操作数组时要避免指针越界,严格控制访问范围。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online