第8章 - 指针基础
嗨,朋友!我是长安。
今天我们要学习 C++ 中最强大、也是最让初学者头疼的概念——指针。
说实话,很多人学 C++ 就是卡在指针这一关。但别担心,我会用最通俗的方式带你搞懂它!
🤔 什么是指针?
生活中的类比
想象你住在一个小区:
- 你的家 = 变量(存储数据的地方)
- 你家的门牌号 = 地址(找到你家的方式)
- 门牌号写在纸条上 = 指针(记录地址的变量)
当我想找你时,我不需要知道你家的具体样子,只需要知道门牌号(地址)就能找到你。
编程中的指针
指针就是一个变量,它存储的是另一个变量的内存地址。
int age = 18; // 普通变量,存储值 18
int* ptr = &age; // 指针变量,存储 age 的地址
age- 普通变量,值是 18&age- 取地址符,获取 age 的内存地址int*- 指针类型,指向 int 的指针ptr- 指针变量,存储 age 的地址
📍 指针的基本操作
1. 定义指针
int* p1; // 指向 int 的指针
double* p2; // 指向 double 的指针
char* p3; // 指向 char 的指针
2. 取地址 (&)
& 运算符用来获取变量的地址。
#include <iostream>
using namespace std;
int main() {
int num = 100;
cout << "num 的值:" << num << endl;
cout << "num 的地址:" << &num << endl;
return 0;
}
输出(地址每次运行可能不同):
num 的值:100
num 的地址:0x7ffeefbff5ac
3. 指针赋值
#include <iostream>
using namespace std;
int main() {
int age = 20;
int* ptr = &age; // ptr 存储 age 的地址
cout << "age 的值:" << age << endl;
cout << "age 的地址:" << &age << endl;
cout << "ptr 存储的地址:" << ptr << endl;
return 0;
}
输出:
age 的值:20
age 的地址:0x7ffeefbff5ac
ptr 存储的地址:0x7ffeefbff5ac
4. 解引用 (*)
* 运算符用来访问指针指向的值(也叫"取内容")。
#include <iostream>
using namespace std;
int main() {
int num = 50;
int* ptr = #
cout << "num 的值:" << num << endl;
cout << "ptr 存储的地址:" << ptr << endl;
cout << "*ptr(指针指向的值):" << *ptr << endl;
// 通过指针修改值
*ptr = 100;
cout << "修改后 num 的值:" << num << endl;
return 0;
}
输出:
num 的值:50
ptr 存储的地址:0x7ffeefbff5ac
*ptr(指针指向的值):50
修改后 num 的值:100
🎨 指针的可视化理解
内存示意图:
地址 变量名 值
0x1000 num 50
0x1004 ptr 0x1000 ──┐
│
└──→ 指向 num
*ptr 就是 num 的值 (50)
ptr 就是 num 的地址 (0x1000)
&num 也是 num 的地址 (0x1000)
🔄 指针与函数
值传递 vs 指针传递
#include <iostream>
using namespace std;
// 值传递 - 不能修改原变量
void changeValue(int x) {
x = 100;
cout << "函数内 x = " << x << endl;
}
// 指针传递 - 可以修改原变量
void changeByPointer(int* p) {
*p = 100;
cout << "函数内 *p = " << *p << endl;
}
int main() {
int num = 50;
cout << "=== 值传递 ===" << endl;
cout << "调用前 num = " << num << endl;
changeValue(num);
cout << "调用后 num = " << num << endl;
cout << "\n=== 指针传递 ===" << endl;
num = 50; // 重置
cout << "调用前 num = " << num << endl;
changeByPointer(&num);
cout << "调用后 num = " << num << endl;
return 0;
}
输出:
=== 值传递 ===
调用前 num = 50
函数内 x = 100
调用后 num = 50
=== 指针传递 ===
调用前 num = 50
函数内 *p = 100
调用后 num = 100
交换两个数(必须用指针)
#include <iostream>
using namespace std;
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 10, y = 20;
cout << "交换前:x = " << x << ", y = " << y << endl;
swap(&x, &y);
cout << "交换后:x = " << x << ", y = " << y << endl;
return 0;
}
输出:
交换前:x = 10, y = 20
交换后:x = 20, y = 10
📦 指针与数组
数组名本质上就是一个指针,指向数组的第一个元素。
#include <iostream>
using namespace std;
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int* ptr = arr; // 数组名就是指针
cout << "通过数组访问:" << endl;
for (int i = 0; i < 5; i++) {
cout << "arr[" << i << "] = " << arr[i] << endl;
}
cout << "\n通过指针访问:" << endl;
for (int i = 0; i < 5; i++) {
cout << "*(ptr + " << i << ") = " << *(ptr + i) << endl;
}
return 0;
}
输出:
通过数组访问:
arr[0] = 10
arr[1] = 20
arr[2] = 30
arr[3] = 40
arr[4] = 50
通过指针访问:
*(ptr + 0) = 10
*(ptr + 1) = 20
*(ptr + 2) = 30
*(ptr + 3) = 40
*(ptr + 4) = 50
指针运算
#include <iostream>
using namespace std;
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int* ptr = arr;
cout << "*ptr = " << *ptr << endl; // 10
ptr++; // 指针移动到下一个元素
cout << "*ptr = " << *ptr << endl; // 20
ptr += 2; // 指针向后移动2个位置
cout << "*ptr = " << *ptr << endl; // 40
return 0;
}
⚠️ 空指针和野指针
空指针 (nullptr)
空指针不指向任何有效内存。
#include <iostream>
using namespace std;
int main() {
int* ptr = nullptr; // C++11 推荐用 nullptr
if (ptr == nullptr) {
cout << "指针为空,不能使用" << endl;
} else {
cout << "*ptr = " << *ptr << endl;
}
return 0;
}
野指针(危险!)
野指针是指向未知内存的指针,非常危险!
int* ptr; // ❌ 未初始化,是野指针
*ptr = 100; // ❌ 危险!可能导致程序崩溃
// 正确做法:
int* ptr = nullptr; // ✅ 初始化为空指针
🌟 实战案例
案例1:通过指针修改数组元素
#include <iostream>
using namespace std;
void doubleArray(int* arr, int size) {
for (int i = 0; i < size; i++) {
*(arr + i) *= 2; // 或 arr[i] *= 2;
}
}
int main() {
int numbers[5] = {1, 2, 3, 4, 5};
cout << "原数组:";
for (int i = 0; i < 5; i++) {
cout << numbers[i] << " ";
}
cout << endl;
doubleArray(numbers, 5);
cout << "翻倍后:";
for (int i = 0; i < 5; i++) {
cout << numbers[i] << " ";
}
cout << endl;
return 0;
}
输出:
原数组:1 2 3 4 5
翻倍后:2 4 6 8 10
案例2:查找数组中的最大值和最小值
#include <iostream>
using namespace std;
void findMinMax(int* arr, int size, int* min, int* max) {
*min = *max = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] < *min) {
*min = arr[i];
}
if (arr[i] > *max) {
*max = arr[i];
}
}
}
int main() {
int numbers[] = {45, 23, 67, 12, 89, 34};
int size = 6;
int minVal, maxVal;
findMinMax(numbers, size, &minVal, &maxVal);
cout << "最小值:" << minVal << endl;
cout << "最大值:" << maxVal << endl;
return 0;
}
输出:
最小值:12
最大值:89
案例3:反转数组
#include <iostream>
using namespace std;
void reverseArray(int* arr, int size) {
int* start = arr;
int* end = arr + size - 1;
while (start < end) {
int temp = *start;
*start = *end;
*end = temp;
start++;
end--;
}
}
int main() {
int arr[] = {1, 2, 3, 4, 5, 6};
int size = 6;
cout << "原数组:";
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
reverseArray(arr, size);
cout << "反转后:";
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
输出:
原数组:1 2 3 4 5 6
反转后:6 5 4 3 2 1
案例4:字符串长度(C风格)
#include <iostream>
using namespace std;
int stringLength(char* str) {
int count = 0;
while (*str != '\0') {
count++;
str++;
}
return count;
}
int main() {
char name[] = "Hello";
cout << "字符串:" << name << endl;
cout << "长度:" << stringLength(name) << endl;
return 0;
}
输出:
字符串:Hello
长度:5
案例5:指针作为函数返回值
#include <iostream>
using namespace std;
int* findMax(int* arr, int size) {
int* maxPtr = arr;
for (int i = 1; i < size; i++) {
if (arr[i] > *maxPtr) {
maxPtr = &arr[i];
}
}
return maxPtr;
}
int main() {
int numbers[] = {23, 67, 45, 89, 12};
int size = 5;
int* maxPtr = findMax(numbers, size);
cout << "最大值:" << *maxPtr << endl;
cout << "最大值的地址:" << maxPtr << endl;
return 0;
}
🎯 小结
这一章我们学习了指针的基础知识:
| 概念 | 说明 | 示例 |
|---|---|---|
| 指针定义 | 存储地址的变量 | int* ptr; |
| 取地址 & | 获取变量地址 | &num |
| 解引用 * | 访问指针指向的值 | *ptr |
| 指针赋值 | 让指针指向某个变量 | ptr = # |
| 空指针 | 不指向任何内存 | ptr = nullptr; |
| 指针运算 | 指针的加减移动 | ptr++ |
| 指针与数组 | 数组名就是指针 | int* ptr = arr; |
| 指针与函数 | 通过指针修改原变量 | func(&num) |
重要提醒:
- ⚠️ 指针使用前必须初始化
- ⚠️ 不要使用野指针
- ⚠️ 不要解引用空指针
- ⚠️ 注意指针的类型要匹配
💪 练习题
动手练习巩固知识:
- 写一个函数,接收两个整数指针,返回较大值的地址
- 写一个函数,使用指针交换三个变量的值(a→b, b→c, c→a)
- 写一个函数,使用指针统计数组中正数、负数、零的个数
- 写一个函数,使用指针复制一个数组到另一个数组
- 写一个函数,使用指针判断一个字符串是否是回文
点击查看参考答案
题目1 参考答案:
#include <iostream>
using namespace std;
int* getMaxPtr(int* a, int* b) {
return (*a > *b) ? a : b;
}
int main() {
int x = 10, y = 20;
int* maxPtr = getMaxPtr(&x, &y);
cout << "较大的值:" << *maxPtr << endl;
return 0;
}
题目3 参考答案:
#include <iostream>
using namespace std;
void countNumbers(int* arr, int size, int* positive, int* negative, int* zero) {
*positive = *negative = *zero = 0;
for (int i = 0; i < size; i++) {
if (arr[i] > 0) {
(*positive)++;
} else if (arr[i] < 0) {
(*negative)++;
} else {
(*zero)++;
}
}
}
int main() {
int arr[] = {1, -2, 0, 5, -3, 0, 7};
int size = 7;
int pos, neg, zer;
countNumbers(arr, size, &pos, &neg, &zer);
cout << "正数:" << pos << endl;
cout << "负数:" << neg << endl;
cout << "零:" << zer << endl;
return 0;
}
🚀 下一步
恭喜你掌握了指针的基础!指针是 C++ 的核心,理解它需要时间和练习。
下一章,我们要学习引用和动态内存管理,这是指针的进阶应用!
