C++: 指针与数组

最后更新:2026-08-26

第16课我们学过数组,但没讲数组名到底是什么。

其实,数组名就是指针——它存的是数组首元素的地址。

理解这一点,就能理解为什么数组作为参数时会"退化成指针"。


1. 数组名就是指针

(1) 1.1 基本事实

数组名就是指向首元素的指针。

CPP
#include <iostream>

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 
 std::cout << "arr = " << arr << std::endl;
 std::cout << "&arr[0] = " << &arr[0] << std::endl;
 // 两者输出一样!因为 arr 就是 &arr[0]
 
 return 0;
}

输出(示例):

TEXT 📖 仅展示
arr = 0x7ffd4a3b
&arr[0] = 0x7ffd4a3b

💡 重点: arr&arr[0]同一个地址



2. 用指针访问数组元素

(1) 2.1 基本用法

CPP
#include <iostream>

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 int* p = arr; // p 指向首元素
 
 // 方法一:用下标(和数组一样)
 std::cout << "arr[0] = " << p[0] << std::endl; // 10
 std::cout << "arr[1] = " << p[1] << std::endl; // 20
 
 // 方法二:用解引用
 std::cout << "*p = " << *p << std::endl; // 10
 std::cout << "*(p+1) = " << *(p+1) << std::endl; // 20
 
 return 0;
}

💡 重点: p[i] 其实就是 *(p+i)(后面会讲指针算术)。



3. 指针算术(Pointer Arithmetic)

(1) 3.1 指针加减整数

指针加减整数时,不是简单地加减地址值,而是加减 整数 × sizeof(类型) 个字节。

CPP
#include <iostream>

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 int* p = arr; // 指向 arr[0]
 
 std::cout << "p = " << p << std::endl; // 假设 0x1000
 std::cout << "p + 1 = " << p + 1 << std::endl; // 0x1004(不是 0x1001!)
 std::cout << "p + 2 = " << p + 2 << std::endl; // 0x1008
 
 return 0;
}

原理:


(2) 3.2 指针相减

两个指针相减,得到的是它们之间的距离(元素个数),不是字节数。

CPP
#include <iostream>

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 int* p1 = &arr[0]; // 指向 arr[0]
 int* p2 = &arr[3]; // 指向 arr[3]
 
 std::cout << "p2 - p1 = " << p2 - p1 << std::endl; // 3(元素个数)
 
 return 0;
}

💡 重点: 只有指向同一个数组的指针相减才有意义。



4. 用指针遍历数组(三种方法)

▶ 示例 1:遍历数组(难度⭐)

CPP
#include <iostream>

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 
 // 方法一:用下标(最常用)
 std::cout << "方法一:下标" << std::endl;
 for (int i = 0; i < 5; i++) {
 std::cout << arr[i] << " ";
 }
 std::cout << std::endl;
 
 // 方法二:用指针算术
 std::cout << "方法二:指针算术" << std::endl;
 for (int* p = arr; p < arr + 5; p++) {
 std::cout << *p << " ";
 }
 std::cout << std::endl;
 
 // 方法三:用标准库函数(后面会学)
 // std::cout << "方法三:std::begin/end" << std::endl;
 // for (int* p = std::begin(arr); p != std::end(arr); p++) {
 // std::cout << *p << " ";
 // }
 
 return 0;
}
▶ 试一试

输出:

TEXT 📖 仅展示
方法一:下标
10 20 30 40 50 
方法二:指针算术
10 20 30 40 50 

💡 推荐: 方法一(下标)最直观,优先用。方法二(指针算术)在底层代码里常见。



5. 数组作为参数时的退化

(1) 5.1 问题:数组会退化成指针

当你把数组传给函数时,数组会退化成指针(丢失大小信息)。

CPP
#include <iostream>

void printSize(int arr) {
 // arr 其实是指针,不是数组!
 std::cout << "sizeof(arr) = " << sizeof(arr) << std::endl; // 8(64位系统,指针大小)
}

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 
 std::cout << "sizeof(arr) = " << sizeof(arr) << std::endl; // 20(5 × 4 = 20 字节)
 printSize(arr);
 
 return 0;
}

💡 重点: printSize 函数里的 arr 其实是指针,所以 sizeof(arr) 是指针的大小(8 字节),不是数组的大小(20 字节)。


(2) 5.2 解决方法:传数组大小

CPP
#include <iostream>

// 必须额外传数组大小
void printArray(int arr, int size) {
 for (int i = 0; i < size; i++) {
 std::cout << arr[i] << " ";
 }
 std::cout << std::endl;
}

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 printArray(arr, 5); // 传数组名(就是首元素地址)
 return 0;
}

💡 提示: 后面会学 std::arraystd::vector,它们能保留大小信息,更安全。



6. 实战:反转数组

▶ 示例 2:用指针反转数组(难度⭐⭐)

CPP
#include <iostream>

void reverseArray(int* arr, int size) {
 int* left = arr; // 指向第一个元素
 int* right = arr + size - 1; // 指向最后一个元素
 
 while (left < right) {
 // 交换 left 和 right 指向的值
 int temp = *left;
 *left = *right;
 *right = temp;
 
 left++; // 向右移动
 right--; // 向左移动
 }
}

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 
 std::cout << "反转前:";
 for (int i = 0; i < 5; i++) {
 std::cout << arr[i] << " ";
 }
 std::cout << std::endl;
 
 reverseArray(arr, 5);
 
 std::cout << "反转后:";
 for (int i = 0; i < 5; i++) {
 std::cout << arr[i] << " ";
 }
 std::cout << std::endl;
 
 return 0;
}
▶ 试一试

输出:

TEXT 📖 仅展示
反转前:10 20 30 40 50 
反转后:50 40 30 20 10 


7. 常见错误

(1) 7.1 指针越界

CPP
#include <iostream>

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 int* p = arr;
 
 std::cout << *(p + 10) << std::endl; // ❌ 越界!p+10 指向数组外
 // 输出垃圾值,或程序崩溃
 
 return 0;
}

💡 提示: C++ 不会自动检查指针越界!这是 C++ 速度快的原因之一,但也是常见的 bug 来源。


(2) 7.2 指针相减没有意义

CPP
#include <iostream>

int main() {
 int x = 5;
 int y = 10;
 int* p1 = &x;
 int* p2 = &y;
 
 std::cout << p2 - p1 << std::endl; // ❌ 未定义行为!p1 和 p2 指向不同数组
 // 只有指向同一个数组的指针相减才有意义
 
 return 0;
}

❓ 常见问题

Q 为什么 arr&arr 的值一样,但类型不同?
A > - arr 的类型是 int*(指向 int 的指针) > - &arr 的类型是 int(*)[5](指向"包含5个int的数组"的指针)
Q 指针算术能用 */ 吗?
A 不能!指针只能做: > - 加减整数(p + np - n) > - 两个指针相减(p2 - p1) > - 比较大小(p1 < p2

▶ 示例 3:指针遍历数组(难度⭐)

CPP
#include <iostream>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int* p = arr;

    for (int i = 0; i < 5; i++) {
        std::cout << "*(p+" << i << ")=" << *(p + i) << std::endl;
    }

    return 0;
}
▶ 试一试

输出:

TEXT 📖 仅展示
*(p+0)=10
*(p+1)=20
*(p+2)=30
*(p+3)=40
*(p+4)=50
💡 提示*(p + i) 等价于 p[i]arr[i]。指针加减整数会自动考虑类型大小。



📖 小节

📝 作业

  1. 基础题 (Difficulty ⭐): 声明一个 int 数组 arr[5] = {1, 2, 3, 4, 5},再声明一个 int* 指针 p 指向 arr。用指针输出所有元素。

  2. 进阶题 (Difficulty ⭐⭐): 写一个函数 int* findElement(int* arr, int size, int target),在数组中查找目标值。如果找到,返回指向该元素的指针;如果没找到,返回 nullptr

  3. 挑战题 (Difficulty ⭐⭐⭐): 写一个程序,用指针实现"冒泡排序":

  4. 遍历数组,比较相邻元素

  5. 如果顺序不对,交换它们

  6. 重复这个过程,直到数组有序


8. 🚀 下一步

学会了指针与数组,接下来我们学习 指针算术进阶(第24课)—— 深入理解指针的类型、void* 指针、以及多级指针。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏