C++ 从入门到精通C++ 从入门到精通
首页
基础教程
进阶教程
实战项目
编程指南
首页
基础教程
进阶教程
实战项目
编程指南
  • 💪 进阶基础

    • 📚 C++ 进阶教程
    • 第8章 - 指针基础
    • 第9章 - 引用和动态内存
    • 第10章 - 结构体和枚举
    • 第11章 - 文件操作
    • 第12章 - 预处理器和命名空间
  • 🎯 面向对象编程

    • 第13章 - 类和对象基础
    • 第14章 - 构造函数和析构函数
    • 第15章 - 封装、继承、多态
    • 第16章 - 运算符重载
    • 第17章 - 模板基础
    • 第18章 - 异常处理
  • 📦 STL 标准模板库

    • 第19章 - Vector 和 String
    • 第20章 - 容器类
    • 第21章 - Map 和 Set
    • 第22章 - 算法和迭代器

第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 = &num;
    
    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 = &num;
空指针不指向任何内存ptr = nullptr;
指针运算指针的加减移动ptr++
指针与数组数组名就是指针int* ptr = arr;
指针与函数通过指针修改原变量func(&num)

重要提醒:

  • ⚠️ 指针使用前必须初始化
  • ⚠️ 不要使用野指针
  • ⚠️ 不要解引用空指针
  • ⚠️ 注意指针的类型要匹配

💪 练习题

动手练习巩固知识:

  1. 写一个函数,接收两个整数指针,返回较大值的地址
  2. 写一个函数,使用指针交换三个变量的值(a→b, b→c, c→a)
  3. 写一个函数,使用指针统计数组中正数、负数、零的个数
  4. 写一个函数,使用指针复制一个数组到另一个数组
  5. 写一个函数,使用指针判断一个字符串是否是回文
点击查看参考答案

题目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++ 的核心,理解它需要时间和练习。

下一章,我们要学习引用和动态内存管理,这是指针的进阶应用!

➡️ 第9章 - 引用和动态内存

最近更新: 2025/12/26 17:25
Contributors: 王长安
Prev
📚 C++ 进阶教程
Next
第9章 - 引用和动态内存