2 指针与数组
正在加载视频播放器…
GESP C++ 四级:指针与数组精讲
核心目标:理解数组名的本质,掌握用指针访问数组元素、指针运算、指针与数组作为函数参数、字符指针与字符串,能熟练判断指针相关程序的输出。
数组名就是首地址;arr[i]等价于*(arr + i);指针运算以元素大小为单位;数组传参传的是首地址,函数内修改会影响原数组。
一、考纲定位
GESP 四级考纲对指针与数组的要求:
- 理解 C++ 指针类型的概念,掌握指针类型变量的定义、赋值、解引用。
- 掌握指针与数组的关系,能用指针访问数组元素。
- 掌握函数参数的传递方式:值传递、引用传递、指针传递。
- 掌握字符型指针变量与字符串的关系。
- 了解二维数组与指针的关系(四级要求掌握二维数组基本概念)。
四级不考:动态内存分配(new/delete)、函数指针、复杂指针数组。
二、数组名的本质
1. 数组名就是首地址
数组名代表数组第一个元素的地址,是一个常量指针。
int arr[5] = {10, 20, 30, 40, 50};
cout << arr << endl; // 输出首地址,如 0x7ffee4065820
cout << &arr[0] << endl; // 与 arr 相同
2. 数组名不能重新赋值
int arr[5];
int *p = arr; // ✅ 正确
arr = p; // ❌ 错误:数组名是常量,不能赋值
3. arr 与 &arr 的区别(了解)
| 表达式 | 类型 | 含义 | +1 移动的字节数 |
|---|---|---|---|
arr |
int* |
指向首元素 | sizeof(int) = 4 |
&arr |
int(*)[5] |
指向整个数组 | sizeof(arr) = 20 |
int arr[5];
cout << (arr + 1) - arr << endl; // 1(移动一个元素)
cout << (&arr + 1) - &arr << endl; // 1(移动整个数组)
三、指针访问数组
1. 指针指向数组
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr; // p 指向 arr[0]
2. 三种访问方式等价
| 数组下标 | 指针形式 | 说明 |
|---|---|---|
arr[i] |
*(arr + i) |
数组名 + 偏移 |
p[i] |
*(p + i) |
指针 + 偏移 |
*(p + i) |
p[i] |
完全等价 |
for (int i = 0; i < 5; i++) {
cout << arr[i] << " "; // 方式一
cout << *(arr + i) << " "; // 方式二
cout << *(p + i) << " "; // 方式三
cout << p[i] << " "; // 方式四
}
3. 用指针遍历数组
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;
// 方式一:指针 + 下标
for (int i = 0; i < 5; i++) {
cout << *(p + i) << " ";
}
// 方式二:指针自增
for (int *q = arr; q < arr + 5; q++) {
cout << *q << " ";
}
四、指针运算
1. 指针加减整数
指针加减的单位是所指向类型的大小。
int arr[3] = {10, 20, 30};
int *p = arr; // 假设地址为 1000
p++; // p 变为 1004(假设 int 占 4 字节)
cout << *p << endl; // 20
2. 指针相减
两个同类型指针相减,得到它们之间相差的元素个数。
int arr[5] = {1, 2, 3, 4, 5};
int *p1 = &arr[1];
int *p2 = &arr[4];
cout << p2 - p1 << endl; // 3
3. 指针比较
同类型指针可以用 > < >= <= == != 比较地址大小。
int *p = arr;
while (p < arr + 5) {
cout << *p << " ";
p++;
}
五、指针与数组作为函数参数
1. 数组参数退化为指针
当数组作为函数参数时,实际传递的是数组首地址,函数内部修改数组元素会影响原数组。
void modify(int arr[], int n) {
arr[0] = 100;
}
int main() {
int a[3] = {1, 2, 3};
modify(a, 3);
cout << a[0]; // 100
}
以下三种函数声明完全等价:
void f(int arr[]);
void f(int arr[10]);
void f(int *arr);
2. 函数内求数组长度是错的
因为数组参数退化为指针,sizeof(arr) 得到的是指针大小,不是数组总字节数。
void f(int arr[]) {
cout << sizeof(arr) << endl; // 输出 8(64位系统指针大小)
}
int main() {
int a[10];
cout << sizeof(a) << endl; // 输出 40
f(a); // 输出 8
}
结论:数组作为参数时,必须额外传递长度 n。
3. 指针传递修改实参
void change(int *p) {
*p = 100;
}
int main() {
int n = 7;
change(&n);
cout << n; // 100
}
4. 交换两个变量(指针版)
void xchg(int *a, int *b) {
int t = *a;
*a = *b;
*b = t;
}
int main() {
int x = 10, y = 20;
xchg(&x, &y);
cout << x << " " << y; // 20 10
}
六、字符指针与字符串
1. 字符指针指向字符串常量
char *s = "Hello";
cout << s << endl; // 输出 Hello
cout << *s << endl; // 输出 H
cout << *(s + 1) << endl; // 输出 e
注意:s 指向字符串常量的首地址,字符串常量存储在只读区,不能修改。
s[0] = 'h'; // ❌ 错误:修改字符串常量,程序崩溃
2. 字符数组与字符指针的区别
| 对比项 | char s[] = "Hello"; |
char *s = "Hello"; |
|---|---|---|
| 存储位置 | 栈区,可修改 | 只读区,不可修改 |
| 大小 | sizeof(s) = 6 |
sizeof(s) = 8(指针大小) |
| 可修改 | ✅ | ❌ |
| 赋值 | 需用 strcpy |
可直接指向其他字符串 |
3. 字符指针遍历字符串
char *s = "Hello";
while (*s != '\0') {
cout << *s;
s++;
}
// 输出 Hello
七、指针数组与数组指针(了解)
1. 指针数组
数组的每个元素都是指针。
int a = 1, b = 2, c = 3;
int *arr[3] = {&a, &b, &c}; // 指针数组
cout << *arr[0] << endl; // 1
2. 数组指针
指向整个数组的指针。
int arr[5] = {1, 2, 3, 4, 5};
int (*p)[5] = &arr; // 数组指针,指向整个数组
cout << (*p)[0] << endl; // 1
3. 快速区分
| 写法 | 名称 | 本质 |
|---|---|---|
int *p[5] |
指针数组 | 数组,元素是指针 |
int (*p)[5] |
数组指针 | 指针,指向数组 |
记忆:[] 优先级高于 *,*p[5] 先结合 [],是指针数组;加括号 (*p)[5] 先结合 *,是数组指针。
八、GESP 四级真题示例
1. 指针与数组地址计算
假设整数数组
arr[4] = {0, 1, 2, 3};的第一个元素在内存中的地址为0x7ffee4065820,经过int* p = arr; p += 1;后,指针p的值是( )。
答案:0x7ffee4065824(假设 int 占 4 字节)
2. 指针传参判断
执行下面程序后,输出为( )。
void fun(int a, int &b, int *c){ a += 1; b += 2; *c += 3; } int main(){ int x = 1, y = 1, z = 1; fun(x, y, &z); cout << x << " " << y << " " << z; }答案:1 3 4
3. 指针与数组输出
以下代码的输出是( )。
int a[3] = {1, 2, 3}; int *p = a; cout << *(p + 2);答案:3
4. 数组名与指针区别
以下说法正确的是( )。
A) 数组名可以重新赋值
B)arr[i]等价于*(arr + i)
C) 数组名是一个指针变量
D)sizeof(arr)在函数参数中仍能得到数组总大小
答案:B
九、易错点汇总
- 数组名不能赋值:
arr = p;错误。 - 数组参数退化为指针:函数内
sizeof(arr)得到指针大小,不是数组大小。 - 指针运算单位错误:
p + 1跳过sizeof(类型)字节,不是1字节。 - 字符指针指向字符串常量不可修改:
char *s = "Hello"; s[0] = 'h';错误。 - 混淆指针数组与数组指针:
int *p[5]是指针数组,int (*p)[5]是数组指针。 - 解引用空指针或野指针:使用前必须初始化并判空。
*(p + i)与*p + i混淆:前者是p[i],后者是p[0] + i。- 指针相减得到元素个数,不是字节数:
p2 - p1返回的是元素个数。 - 函数内修改指针本身 vs 修改指针指向的值:
p = &b只改局部指针;*p = 20才改外面。 - 字符串常量与字符数组混淆:
char s[] = "Hello"可修改,char *s = "Hello"不可修改。
十、速查表
| 需求 | 写法 | 说明 |
|---|---|---|
| 数组首地址 | arr 或 &arr[0] |
类型 int* |
| 指针指向数组 | int *p = arr; |
p 指向 arr[0] |
| 访问元素 | *(p + i) 或 p[i] |
等价于 arr[i] |
| 指针自增 | p++ |
移动一个元素 |
| 指针相减 | p2 - p1 |
返回元素个数 |
| 数组作参数 | void f(int arr[], int n) |
等价于 int *arr |
| 函数内修改数组 | arr[0] = 100; |
影响原数组 |
| 指针传参修改实参 | void f(int *p) { *p = 100; } |
调用 f(&x) |
| 字符指针 | char *s = "Hello"; |
不可修改内容 |
| 字符数组 | char s[] = "Hello"; |
可修改 |
| 指针数组 | int *p[5]; |
数组元素是指针 |
| 数组指针 | int (*p)[5]; |
指向整个数组 |
十一、总结
指针与数组是GESP四级的核心考点。
- 数组名是首地址,是常量,不能赋值。
arr[i]等价于*(arr + i),也等价于p[i]。- 指针运算以所指向类型的大小为单位。
- 两个同类型指针相减,得到元素个数。
- 数组作函数参数时退化为指针,必须额外传长度。
- 函数内通过指针可以修改外面的变量。
- 字符指针指向字符串常量时,内容不可修改。
int *p[5]是指针数组,int (*p)[5]是数组指针。*(p + i)是取第 i 个元素,*p + i是首元素值加 i。- 多练“地址计算”“指针传参判断”“指针与数组结合输出”三类题。
复习建议:先理解数组名与指针的关系,再练指针遍历数组、指针运算、指针传参,最后区分字符指针与字符数组。考试中先判断指针指向哪里,再看是值传递、引用传递还是指针传递,最后逐行跟踪变量变化。