详解C++中的指针、数组指针与函数指针

阿橙
阿橙
发布时间:2026-10-13| 6人浏览过
原创

C++ 通过指针实现了高效的内存操作,但也伴随着潜在风险。本文深入解析 C++ 中变量指针、数组指针和函数指针的定义方式与核心用途,结合完整的代码实例展示从地址获取到函数调用的全过程,并系统梳理了数组指针与指针数组、引用传递与指针传递等关键概念的区别,旨在帮助读者建立清晰且安全的 C++ 内存管理认知。

理解指针与基础类型

在 C++ 程序中,所有的变量、常量以及函数都存储在特定的内存地址中。指针(Pointer)是一种特殊的变量,专门用于存储另一个变量的内存地址。它不仅能读取地址,还能间接操作该地址指向的内存数据。

除了 void* 这种通用空类型指针外,普通指针的数据类型必须与其所指向变量的数据类型保持一致。例如,int* 只能指向整型变量的地址。正确使用指针能让程序运行更高效,但使用不当(如解引用未初始化或越界的指针)会导致严重的运行时错误或安全漏洞。

三类指针的定义与方法

C++ 中最常见的指针分为三种:变量指针、数组指针和函数指针。下面分别说明其定义方式:

第1步:定义并使用变量指针

目的:获取单个变量的内存地址并进行读写访问。

核心操作:

  • 声明:int* p; 声明一个指向整型的指针变量。
  • 初始化(可选):p = 0; 将指针初始化为空地址(NULL),防止悬垂指针。
  • 获取地址:使用取地址符 &,如 p = &a;,让指针指向变量 a。
  • 访问内容:使用解引用符 *,如 *p,读取或修改 a 的值。

第2步:定义并初始化数组指针

目的:使指针指向数组的首地址,从而遍历或访问数组元素。

核心操作:

  • 定义数组:int a[] = {0, 1, 2};。
  • 指向首地址:int* p = a;。注意,数组名 a 在表达式中退化为首元素的地址,即等价于 &a[0]。

第3步:定义并调用函数指针

目的:通过指针对函数进行间接调用,常用于回调函数或动态分发机制。

核心操作:

  • 定义函数原型:int f();。
  • 定义函数指针:int (*pf)();。这里括号至关重要,确保 pf 先与 * 结合成为指针,再返回 int 类型。
  • 赋值函数地址:pf = f;。直接将函数名赋给指针。
  • 调用函数:(*pf)();。通过解引用函数指针来执行函数体。

以下代码完整演示了上述三种指针的使用场景及输出结果:

#include<iostream>
using namespace std;
 
// 定义被调用的函数
int f() 
{
  cout<<"测试函数指针的使用"<<endl<<endl;
  return 0;
}
 
void main()
{
  // ========== 变量指针的使用 ==========
  cout<<"==========变量指针的使用=========="<<endl;
  int a = 5;
  int* p = 0;  // 初始化指针为0
  int* q;      // 仅声明指针
  q = &a;      // 赋值指针,q现在指向a

  cout<<"a = "<<a<<endl;          // 直接输出变量值:5
  cout<<"a = "<<*q<<endl;        // 通过指针访问值:5
  cout<<"p = "<<p<<endl;          // 指针的地址值为0
  cout<<"&a = "<<&a<<endl;       // 直接获取a的物理地址
  cout<<"&a = "<<q<<endl;        // 通过指针获取a的物理地址(应与上一行一致)
 
  // ========== 数组指针的使用 ==========
  cout<<"==========数组指针的使用=========="<<endl;
  int b[] = {0,1,2,3,4,5,6,7,8,9};
  int* pb = b;   // 数组指针指向b的首地址

  cout<<pb<<endl     // 第1个元素的地址
    <<b<<endl       // 数组名即首地址
    <<*pb<<endl     // 第1个元素的值(0)
    <<(*pb+2)<<endl; // 第1个元素值加2(结果为2)
 
  // ========== 函数指针的使用 ==========
  cout<<"==========函数指针的使用=========="<<endl;
  int (*pf)();       // 定义函数指针
  pf = f;            // 将函数f的地址赋值给pf
  (*pf)();           // 通过指针调用函数
}

执行上述代码后,控制台将首先打印出各变量及其地址信息,随后执行函数指针调用的输出语句,结果如下所示:

数组指针 vs 指针数组

这是初学者容易混淆的两个概念,本质区别在于“谁是指针”:

  • 数组指针(Array Pointer):本质是一个指针。它只占一个指针的空间(通常是 4 字节或 8 字节),用来指向整个数组的起始地址。定义为 int (*p)[N];。
  • 指针数组(Pointer Array):本质是一个数组。它是一组存放指针的容器,数组内的每一个元素都是指向某个对象的指针。定义为 int* p[N];。

函数指针 vs 指针函数

区分的关键在于优先级与词意:

  • 函数指针(Function Pointer):本质是指针。指向一段函数代码的入口地址。常用于实现策略模式或异步回调。
  • 指针函数(Pointer Function):本质是函数。其返回值是一个指针。例如 int* get_pointer();。

参数传递:值、引用与指针

C++ 中函数参数的传递机制直接影响数据的可见性与修改范围:

1. 值传递(Value Pass)

形参是实参的一份完整拷贝。对形参的任何修改都不会影响实参。这种方式安全性最高,但在传递大型对象时会有性能开销。

2. 指针传递(Pointer Pass)

传递的是实参的地址(指针)。函数内部可以通过解引用操作直接修改外部实参的内容。由于只传递了地址(固定大小),适合处理大数据结构。

3. 引用传递(Reference Pass)

形参是实参的一个别名(Alias)。在语法上看起来像直接操作局部变量,但实际上底层是通过访问实参的内存地址实现的。对形参的操作等同于直接操作实参。引用必须在定义时初始化,且一旦绑定不可更改。

总结指针与引用的核心差异:

  • 实体性质:指针是一个独立变量(保存地址),引用是已存在变量的别名。
  • 可变性:指针可以随时改变指向(见异思迁),引用一经定义便不能改变目标(从一而终)。
  • 空状态:指针可以为 NULL,引用必须指向有效对象。
  • sizeof 行为:sizeof(指针) 得到的是指针本身的大小;sizeof(引用) 得到的是其所指变量的大小。
  • 自增运算:指针自增指向下一个内存单元;引用自增意味着所指对象值的增加。
  • 安全性:引用是类型安全的,编译器会进行更多检查;指针灵活性高但容易出错。

以上就是C++中指针、数组指针与函数指针的详细解析与对比,更多关于C++基础知识点的应用技巧请持续关注本站!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。