C++指针与数组
中等19C++指针与数组:用门牌号逛遍整条数字街
指针和数组是C++里一对特别要好的朋友。简单来说,数组是内存中一排连续的房间,每个房间里存了一个数字(或其他数据);指针就像一张写着门牌号的纸条,你拿着它就能找到对应的房间。如果你有一排这样的房间(数组),只要知道第一个房间的门牌号,然后不断加1,就能依次访问所有房间。这在编程中非常有用,比如你要遍历全班同学的考试成绩、统计一周的零花钱、或者处理一长串游戏得分。
指针和数组的“老交情”
数组的名字本身就是一个“门牌号”——它代表数组第一个元素的地址。比如:
int scores[5] = {80, 90, 75, 88, 92}; // 五门考试的成绩
这里的 scores 就是第一个元素 80 的地址。我们可以创建一个指针变量来保存这个地址:
int *ptr = scores; // ptr 指向 scores 的第一个元素(地址)
现在,*ptr 就能取出第一个成绩:80。就像你说的:“嘿,门牌号为 scores 的房间里住着80分!”
指针算术:一步一个门牌号
指针有一个神奇的能力:加1它会自动跳到下一个元素,而不是简单地加一个字节。这是因为C++知道指针指向的是什么类型的变量,它会自动加上这个类型占用的字节数(比如 int 占4个字节,所以加1实际上是地址加4)。这样你完全不用操心每个房间的大小,指针会聪明地跨过墙壁,准确进入下一个房间。
举个例子,假设我们想从第二个房间开始看:
int *ptr = scores + 1; // ptr 指向第二个元素(地址),即90
cout << *ptr; // 输出 90
同样,ptr++ 也能让指针移动到下一个房间。你可以用循环把整排房间逛一遍:
int *ptr = scores; // 从第一个房间出发
for (int i = 0; i < 5; i++) {
cout << "第 " << i+1 << " 门考试得分: " << *ptr << endl;
ptr++; // 跳到下一个门牌号
}
输出:
第 1 门考试得分: 80
第 2 门考试得分: 90
第 3 门考试得分: 75
第 4 门考试得分: 88
第 5 门考试得分: 92
也可以反过来,用 ptr-- 从后往前逛。指针还能做减法,比如 ptr2 - ptr1 能得到两个指针之间相差几个元素。
常见错误:新手容易踩的坑
❌ 错误1:指针没有初始化就使用
int *ptr; // 指针未初始化,乱指一通
*ptr = 100; // 危险!可能写到程序其他地方,导致崩溃
解决方法:让指针指向一个合法的地址,比如数组名或者用 & 取变量地址。
❌ 错误2:指针越界
int scores[3] = {10, 20, 30};
int *ptr = scores;
for (int i = 0; i <= 3; i++) { // 注意 i<=3,访问了第4个元素(不存在)
cout << *ptr << endl;
ptr++;
}
数组只有3个元素,却访问了第4个“虚拟房间”,结果是未定义行为(可能输出垃圾值,甚至程序崩溃)。一定要循环到 i < 数组长度。
❌ 错误3:混淆数组名和指针
虽然数组名可以当作指针用,但它不是变量,不能做自增运算。例如:
int scores[3] = {1,2,3};
scores++; // 错误!数组名是常量,不能修改
而指针变量可以随意移动:
int *ptr = scores;
ptr++; // 正确,指针可以移动
❌ 错误4:取地址 & 和取内容 * 搞反
&scores[0]得到第一个元素的地址(门牌号)*ptr得到指针指向的内容(房间里住着的数字) 新手常犯:写成ptr = &scores?注意scores本身就是地址,不需要再加&;取某个元素地址则要写&scores[i]。
完整示例:用指针修改数组 + 看地址
下面是一个完整的程序,展示了用指针遍历数组的同时,还能修改元素的值。我们想象有一个“每周零花钱”数组,第一天是10元,第二天是15元……我们用指针把第一天的零花钱翻倍,然后打印所有天的零花钱和对应的门牌号(地址)。
#include <iostream>
using namespace std;
int main() {
// 一周的零花钱(单位:元)
int pocket[7] = {10, 15, 20, 25, 30, 35, 40};
// 用指针修改第一个元素:翻倍
int *ptr = pocket; // ptr指向第一个元素
*ptr = *ptr * 2; // 第一个房间的钱变成20
// 重新让ptr指向数组开头,然后遍历
ptr = pocket; // 指针指向第一个房间
for (int i = 0; i < 7; i++) {
cout << "第" << i+1 << "天 零花钱: " << *ptr;
cout << " 门牌号(地址): " << ptr << endl;
ptr++; // 移到下一个房间
}
return 0;
}
输出示例(地址每次运行可能不同):
第1天 零花钱: 20 门牌号(地址): 0x7ffd3a2b1a40
第2天 零花钱: 15 门牌号(地址): 0x7ffd3a2b1a44
第3天 零花钱: 20 门牌号(地址): 0x7ffd3a2b1a48
第4天 零花钱: 25 门牌号(地址): 0x7ffd3a2b1a4c
第5天 零花钱: 30 门牌号(地址): 0x7ffd3a2b1a50
第6天 零花钱: 35 门牌号(地址): 0x7ffd3a2b1a54
第7天 零花钱: 40 门牌号(地址): 0x7ffd3a2b1a58
注意看地址:每个 int 占4个字节,所以地址每次增加4(十六进制中 0x40 到 0x44 相差4)。这就是指针算术的魔力。
指针与数组的更多玩法
- 指针数组:你可以创建一个数组,里面存的是指针(门牌号)。比如你有多个数组,想用同一个指针数组来指向它们。
- 数组指针:也可以定义一个指针指向整个数组(不常用,但面试可能会考)。
- 用指针作为函数参数:把数组名传给函数时,实际上传的是首地址,函数内部可以用指针操作原数组,避免复制大量数据。
相关指引:下一步学什么?
学会了指针和数组的配合,你已经掌握了一个非常强大的工具。接下来可以探索:
- 指针与字符串:C风格字符串其实就是字符数组,指针可以轻松遍历每个字符。
- 指针与函数:学习如何把指针传给函数,在函数里修改数组(例如排序)。
- 动态内存分配:
new和delete关键字配合指针,可以在程序运行时创建大小可变的“虚拟房间”。 - 指针与结构体:用指针操作结构体数组,就像整理同学通讯录一样。
指针是C++的灵魂,越用越顺手。现在就去写几个小程序练练手吧!
例题精讲
已知int arr[5] = {10, 20, 30, 40, 50}; int *p = arr; 则执行 p += 2; 后,*p 的值是多少?
对于数组int a[4] = {1,2,3,4}; 下列哪个表达式与 &a[2] 相等?
若 int arr[3] = {0}; int *p = arr; 则 p 和 arr 可以互换使用,例如 p[1] 和 arr[1] 均合法且等价。
以下代码通过指针遍历数组并求和。请填空。
int arr[6] = {2,4,6,8,10,12};
int *p = arr;
int sum = 0;
for(int i = 0; i < 6; i++) {
sum += ___(1)___;
___(2)___;
}以下程序输出数组中的所有元素,请填空。
int a[] = {1,2,3,4,5};
int *q = a;
for(int i = 0; i < 5; i++) {
cout << ___(1)___ << " ";
q++;
}