一、数组(Array)
数组是 C 语言中用于存储一组相同类型元素的连续内存结构,可以使用下标访问每个元素。
基本语法
类型 数组名[大小];
示例:
int nums[5]; // 定义一个包含5个int类型的数组,元素从下标0到4
也可以初始化:
int nums[5] = {1, 2, 3, 4, 5};
char str[] = "Hello"; // 实际是 char str[6] = {'H','e','l','l','o','\0'};
数组访问
int arr[3] = {10, 20, 30};
printf("%d\n", arr[0]); // 输出第一个元素
二维数组
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
printf("%d\n", matrix[1][2]); // 输出6
二、指针(Pointer)
指针是 C 语言的核心之一,用于存储变量的地址。它让我们可以间接访问和操作变量。
基本语法
类型 *指针名;
示例:
int a = 10;
int *p = &a; // p 是一个指向 int 类型变量的指针
使用指针访问值
printf("%d\n", *p); // 通过指针p获取a的值
*p = 20; // 修改a的值
空指针与野指针
int *p = NULL; // 空指针,表示尚未指向任何地址
不要使用未初始化的指针,如:
int *q; // 未初始化,危险!可能是“野指针”
三、数组与指针的关系
数组名在大多数表达式中会被解释为指向首元素的指针。
int arr[3] = {1, 2, 3};
int *p = arr; // 等价于 int *p = &arr[0];
你可以用指针访问数组:
printf("%d\n", *(p + 1)); // 输出 arr[1] 的值
也可以用下标操作指针:
printf("%d\n", p[2]); // 输出 arr[2]
数组与指针的本质区别
区别点 | 数组 | 指针 |
---|---|---|
是否占内存 | 占据实际空间 | 只存地址,占固定空间 |
是否可修改 | 数组名是常量指针,不可修改 | 指针变量可重新赋地址 |
使用场景 | 固定大小、顺序访问的数据 | 动态分配、函数传参、遍历等 |
四、数组作为函数参数
数组传参本质上传的是指针!
void printArray(int arr[], int size) {
for(int i = 0; i < size; i++)
printf("%d ", arr[i]);
}
等价写法:
void printArray(int *arr, int size); // 推荐写法,更直观
五、指针数组与数组指针
这两个是很多人搞混的部分,重点在于:
指针数组:是一个数组,里面存的是指针;
数组指针:是一个指针,指向整个数组。
指针数组
char *names[] = {"Alice", "Bob", "Charlie"};
printf("%s\n", names[1]); // 输出 Bob
每个 names[i]
是 char*
,指向字符串的起始地址。
数组指针
int arr[5] = {1, 2, 3, 4, 5};
int (*p)[5] = &arr; // p 是一个指向整型数组的指针
使用方式:
printf("%d\n", (*p)[2]); // 输出3
六、常见误区和注意点
1.数组越界
int a[3] = {1,2,3};
a[5] = 100; // 错误!会导致未定义行为
2.指针指向的内存未初始化
int *p; // 未初始化,不能直接使用
3.数组传参退化为指针,丢失大小信息
void f(int arr[]) {
sizeof(arr); // 实际是指针大小,不是数组大小!
}
七、实用示例:字符串长度函数
int my_strlen(const char *str) {
int len = 0;
while (*str++) len++;
return len;
}
指针操作字符串是 C 中的经典写法。
八、数组和指针在内存中的形象理解
int a = 10;
int *p = &a;
内存中可能是这样的:
地址 | 值 | 说明 |
---|---|---|
0x1000 | 10 | a 的值 |
0x2000 | 0x1000 | p 存储的地址 |
总结
-
数组和指针密切相关,但语义和用法不同;
-
数组名在表达式中退化为指针,但本质不是指针;
-
指针更灵活,支持动态分配和函数传参;
-
理解内存布局对于掌握指针至关重要;
-
指针数组、数组指针的区分是C语言进阶的关键。