CC++ & Algorithm

C++指针与数组

中等19
语言版本:C++Python(暂无)
概述:用“门牌号”和“一排房子”的比喻,解释指针如何指向数组并遍历其中的元素。

C++指针与数组:用门牌号逛遍整条数字街

指针和数组是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(十六进制中 0x400x44 相差4)。这就是指针算术的魔力。

指针与数组的更多玩法

  • 指针数组:你可以创建一个数组,里面存的是指针(门牌号)。比如你有多个数组,想用同一个指针数组来指向它们。
  • 数组指针:也可以定义一个指针指向整个数组(不常用,但面试可能会考)。
  • 用指针作为函数参数:把数组名传给函数时,实际上传的是首地址,函数内部可以用指针操作原数组,避免复制大量数据。

相关指引:下一步学什么?

学会了指针和数组的配合,你已经掌握了一个非常强大的工具。接下来可以探索:

  • 指针与字符串:C风格字符串其实就是字符数组,指针可以轻松遍历每个字符。
  • 指针与函数:学习如何把指针传给函数,在函数里修改数组(例如排序)。
  • 动态内存分配newdelete 关键字配合指针,可以在程序运行时创建大小可变的“虚拟房间”。
  • 指针与结构体:用指针操作结构体数组,就像整理同学通讯录一样。

指针是C++的灵魂,越用越顺手。现在就去写几个小程序练练手吧!

例题精讲

1单选题

已知int arr[5] = {10, 20, 30, 40, 50}; int *p = arr; 则执行 p += 2; 后,*p 的值是多少?

A10
B20
C30
D40
2单选题

对于数组int a[4] = {1,2,3,4}; 下列哪个表达式与 &a[2] 相等?

Aa + 2
B*a + 2
Ca[2]
D&a + 2
3判断题

若 int arr[3] = {0}; int *p = arr; 则 p 和 arr 可以互换使用,例如 p[1] 和 arr[1] 均合法且等价。

4填空题
以下代码通过指针遍历数组并求和。请填空。

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)___;
}
5填空题
以下程序输出数组中的所有元素,请填空。

int a[] = {1,2,3,4,5};
int *q = a;
for(int i = 0; i < 5; i++) {
    cout << ___(1)___ << " ";
    q++;
}