C++指针

梦想不会自己发光,真正闪耀的是那个为梦狂奔的你。献给知行的孩子们!(Eric.He著)


  本教程将从 C++ 指针的核心概念、指针变量的定义与初始化,到指针的运算、高级应用(指针与数组、字符串、函数)等维度,全面拆解 C++ 指针的核心用法,帮助你掌握这一 C++ 编程的核心利器。

教程目录导航

一、指针核心概念

指针是 C++ 中最核心的概念之一,本质是存储内存地址的变量

直观理解:

把计算机内存比作一排带编号的储物柜,变量是储物柜里的物品,内存地址是储物柜编号,指针是写有储物柜编号的纸条。通过纸条(指针)可以找到对应的储物柜(内存地址),进而操作里面的物品(变量值)。

二、指针变量

2.1 指针变量的定义

指针变量的定义语法:


// 基础语法:数据类型* 指针变量名;
数据类型* 指针变量名;

// 示例:
// 定义指向int类型变量的指针p1
int* p1;
// 定义指向float类型变量的指针p2
float* p2;
// 定义指向char类型变量的指针p3
char* p3;
// 定义指向结构体的指针p4(假设已定义Student结构体)
struct Student* p4;
        

注意:

2.2 NULL初始化

指针变量定义后若未明确指向,会成为“野指针”(指向随机内存地址),极易导致程序崩溃。推荐先初始化为 NULL(C++11 后推荐 nullptr),表示指针暂时不指向任何有效内存。


#include <iostream>
using namespace std;

int main() {
    // 初始化指针为NULL(空指针)
    int* p = NULL;
    // C++11及以上推荐使用nullptr
    // int* p = nullptr;
    
    // 空指针不可直接解引用(*p),否则程序崩溃
    if (p == NULL) {
        cout << "指针p是空指针,暂未指向有效内存" << endl;
    }
    return 0;
}
        

空指针特性:

2.3 变量地址初始化

通过 &(取地址运算符)获取变量的内存地址,赋值给指针变量,完成初始化。


#include <iostream>
using namespace std;

int main() {
    // 定义普通变量
    int a = 10;
    // 初始化指针p为变量a的地址(&a表示取a的内存地址)
    int* p = &a;
    
    cout << "变量a的值:" << a << endl;
    // 输出a的内存地址(十六进制)
    cout << "变量a的地址:" << &a << endl;
    // 输出指针p的值(即a的地址)
    cout << "指针p的值:" << p << endl;
    return 0;
}
        

运行结果示例:

变量a的值:10
变量a的地址:0x0065FF18
指针p的值:0x0065FF18
        

2.4 new初始化

new 是 C++ 动态内存分配关键字,用于在堆区分配内存,并返回该内存的地址,可直接初始化指针。


#include <iostream>
using namespace std;

int main() {
    // 动态分配int类型的堆内存,返回地址赋值给p
    int* p = new int;
    // 给堆内存赋值
    *p = 20;
    
    cout << "堆内存中的值:" << *p << endl;
    cout << "堆内存的地址:" << p << endl;
    
    // 释放堆内存(避免内存泄漏)
    delete p;
    // 将指针置为空,避免野指针
    p = nullptr;
    return 0;
}
        

new初始化注意事项:

2.5 指针变量的赋值

指针变量赋值分为两种场景:赋值为“内存地址”、指针间相互赋值。


#include <iostream>
using namespace std;

int main() {
    // 场景1:赋值为变量地址
    int a = 10;
    int* p1 = nullptr;
    // 赋值:p1指向a的地址
    p1 = &a;
    
    // 场景2:指针间相互赋值(类型需一致)
    int* p2 = nullptr;
    // p2指向p1所指向的地址(即a的地址)
    p2 = p1;
    
    cout << "*p1 = " << *p1 << endl; // 输出10
    cout << "*p2 = " << *p2 << endl; // 输出10
    
    // 场景3:赋值为堆内存地址
    int* p3 = new int(30);
    cout << "*p3 = " << *p3 << endl; // 输出30
    
    delete p3;
    p3 = nullptr;
    return 0;
}
        

三、指针的引用

3.1 & 取地址运算符

&(取地址运算符)用于获取变量/对象的内存地址,是指针初始化的核心运算符。


#include <iostream>
using namespace std;

int main() {
    int num = 100;
    double pi = 3.14;
    char ch = 'A';
    
    // 获取不同类型变量的地址
    cout << "num的地址:" << &num << endl;
    cout << "pi的地址:" << &pi << endl;
    cout << "ch的地址:" << &ch << endl;
    
    return 0;
}
        

注意:

3.2 * 间接存取运算符

*(解引用运算符/间接存取运算符)用于通过指针访问其指向的内存中的数据。


#include <iostream>
using namespace std;

int main() {
    int a = 50;
    int* p = &a;
    
    // 解引用:访问p指向的内存中的值(即a的值)
    cout << "*p = " << *p << endl; // 输出50
    
    // 解引用赋值:修改p指向的内存中的值(即修改a的值)
    *p = 100;
    cout << "a = " << a << endl;   // 输出100
    cout << "*p = " << *p << endl; // 输出100
    
    return 0;
}
        

解引用注意事项:

四、指针的运算

指针的运算本质是“内存地址的偏移”,仅支持 +- 算术运算和比较运算,运算单位是“指针指向类型的大小”。

4.1 + 运算

指针 + n 表示:指针指向的地址向后偏移 n * 指针类型大小 个字节。


#include <iostream>
using namespace std;

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    // p指向数组首元素(数组名arr本质是首元素地址)
    int* p = arr;
    
    cout << "p指向的地址:" << p << ",值:" << *p << endl; // 10
    // p+1:偏移1个int大小(4字节),指向arr[1]
    p = p + 1;
    cout << "p+1指向的地址:" << p << ",值:" << *p << endl; // 20
    // p+2:再偏移2个int大小(8字节),指向arr[3]
    p = p + 2;
    cout << "p+2指向的地址:" << p << ",值:" << *p << endl; // 40
    
    return 0;
}
        

运行结果示例(32位系统):

p指向的地址:0x0065FF00,值:10
p+1指向的地址:0x0065FF04,值:20
p+2指向的地址:0x0065FF0C,值:40
        

4.2 - 运算

指针 - n 表示:指针指向的地址向前偏移 n * 指针类型大小 个字节;两个同类型指针相减,表示地址之间的元素个数。


#include <iostream>
using namespace std;

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    // p指向arr[3]
    int* p = &arr[3];
    
    cout << "p指向的地址:" << p << ",值:" << *p << endl; // 40
    // p-2:向前偏移2个int大小(8字节),指向arr[1]
    p = p - 2;
    cout << "p-2指向的地址:" << p << ",值:" << *p << endl; // 20
    
    // 两个指针相减:计算元素个数
    int* p1 = arr;       // 指向arr[0]
    int* p2 = &arr[4];   // 指向arr[4]
    // 结果为4(表示p2 - p1之间有4个int元素)
    int diff = p2 - p1;
    cout << "p2 - p1 = " << diff << endl;
    
    return 0;
}
        

指针运算规则:

五、多重指针

多重指针(又称指针的指针)是存储“指针地址”的指针,核心用于多级间接访问内存,常见的是二级指针(数据类型**)。


#include <iostream>
using namespace std;

int main() {
    int a = 10;
    // 一级指针:指向a的地址
    int* p1 = &a;
    // 二级指针:指向p1的地址
    int** p2 = &p1;
    
    cout << "a的值:" << a << endl;
    cout << "a的地址:" << &a << endl;
    cout << "p1的值(a的地址):" << p1 << ",*p1 = " << *p1 << endl;
    cout << "p1的地址:" << &p1 << endl;
    cout << "p2的值(p1的地址):" << p2 << ",*p2 = " << *p2 << endl;
    // **p2:先解引用p2得到p1,再解引用p1得到a
    cout << "**p2 = " << **p2 << endl;
    
    // 修改a的值(通过二级指针)
    **p2 = 200;
    cout << "修改后a的值:" << a << endl;
    
    return 0;
}
        

运行结果示例:

a的值:10
a的地址:0x0065FF18
p1的值(a的地址):0x0065FF18,*p1 = 10
p1的地址:0x0065FF10
p2的值(p1的地址):0x0065FF10,*p2 = 0x0065FF18
**p2 = 10
修改后a的值:200
        

多重指针的应用场景:

六、指针与数组

数组名本质是“指向数组首元素的常量指针”,通过指针可实现数组的高效遍历和操作。


#include <iostream>
using namespace std;

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int len = sizeof(arr) / sizeof(arr[0]); // 数组长度
    
    // 数组名arr是指向首元素的常量指针
    cout << "arr = " << arr << endl;          // 首元素地址
    cout << "*arr = " << *arr << endl;        // 首元素值(arr[0])
    cout << "arr[2] = " << *(arr + 2) << endl;// 等价于arr[2]
    
    // 指针遍历数组
    int* p = arr;
    for (int i = 0; i < len; i++) {
        // *(p+i) 等价于 arr[i]
        cout << "arr[" << i << "] = " << *(p + i) << " ";
    }
    cout << endl;
    
    // 修改数组元素(通过指针)
    *(p + 3) = 40;
    cout << "修改后arr[3] = " << arr[3] << endl;
    
    return 0;
}
        

指针与数组的核心关系:

七、指针与字符串

C++ 中字符串有两种表示形式:字符数组、字符指针,字符指针是字符串操作的核心方式。


#include <iostream>
#include <cstring> // 字符串函数头文件
using namespace std;

int main() {
    // 方式1:字符数组表示字符串(可修改)
    char str1[] = "Hello C++";
    str1[0] = 'h'; // 合法:修改第一个字符
    cout << "str1 = " << str1 << endl;
    
    // 方式2:字符指针指向字符串常量(不可修改)
    char* str2 = "Hello Pointer";
    // str2[0] = 'h'; // 非法:字符串常量存放在只读内存
    
    // 字符串遍历(指针方式)
    char* p = str1;
    while (*p != '\0') { // '\0'是字符串结束符
        cout << *p;
        p++;
    }
    cout << endl;
    
    // 字符串拷贝(指针应用)
    char str3[20];
    char* p3 = str3;
    // strcpy:将str1拷贝到str3
    strcpy(p3, str1);
    cout << "str3 = " << p3 << endl;
    
    return 0;
}
        

指针与字符串的注意事项:

八、指针与函数

指针在函数中的应用包括:指针作为函数参数、函数返回指针、函数指针(指向函数的指针)。

8.1 指针作为函数参数

实现“传地址调用”,函数可直接修改实参的值,避免拷贝开销。


#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;
}
        

8.2 函数返回指针

函数可返回指针(如堆内存地址、静态变量地址),但禁止返回栈区局部变量的指针。


#include <iostream>
using namespace std;

// 返回堆内存指针
int* createInt(int value) {
    // 堆内存不会随函数结束释放
    int* p = new int(value);
    return p;
}

int main() {
    int* p = createInt(100);
    cout << "*p = " << *p << endl; // 输出100
    
    delete p; // 释放堆内存
    p = nullptr;
    return 0;
}
        

8.3 函数指针

函数指针是指向函数的指针,存储函数的入口地址,用于回调函数、动态函数调用。


#include <iostream>
using namespace std;

// 加法函数
int add(int a, int b) {
    return a + b;
}

// 减法函数
int sub(int a, int b) {
    return a - b;
}

int main() {
    // 定义函数指针:指向返回int、参数为两个int的函数
    int (*funcPtr)(int, int);
    
    // 指向add函数
    funcPtr = add;
    cout << "3 + 5 = " << funcPtr(3, 5) << endl;
    
    // 指向sub函数
    funcPtr = sub;
    cout << "8 - 2 = " << funcPtr(8, 2) << endl;
    
    return 0;
}
        

九、注意事项

十、总结

本教程从指针的核心概念到高级应用,全面拆解了 C++ 指针的用法。掌握指针是突破 C++ 编程瓶颈的关键,也是学习数据结构与算法的核心基础。


返回顶部