数据结构中的指针

14 Aug 2025 | 阅读 14 分钟

存储另一个变量地址的变量称为指针。普通变量存储值。然而,指针保存存储值的内存位置。指针允许我们直接更改和访问系统内存。指针提高了重复过程的性能。

Pointers in Data Structure

C 和 C++ 等语言大量使用指针。然而,其他语言也可以有指针,尽管它们可能不尽相同。

指针示例

在下图中,变量 a 在地址 2000 处存储值 10,变量 b 在地址 3000 处存储空值。但在下一个语句 (b=&a) 指向 a 的地址,这意味着变量 b 存储 a 的地址,即 2000。

Pointers in Data Structure

为什么我们需要数据结构中的指针?

指针有助于减少算法将数据从一个位置复制到另一个位置所需的时间。因此,指针直接使用内存位置;对值的任何更改都将在所有位置反映出来。例如,

  • 值传递:每次需要执行操作时,都需要复制参数的值。
  • 引用传递:通过使用其内存位置来更新内存位置的值,使此任务更容易。
  • 控制程序流:指针还可用于控制程序流。这是通过利用这些指针的控制表来实现的。指针存储在表中,指向每个子程序的入口点,然后这些子程序依次执行。
  • 动态内存分配:大多数编程语言都使用动态内存分配来处理运行时变量。这种类型的内存分配依赖于堆而不是栈,堆使用指针。指针存储这些动态创建的数据块或对象数组的地址。
  • 解引用:访问存储在位置中的值称为使用运算符(例如,C/C++ 中的 *)解引用指针。这将检索指向位置的内容。

指针类型

编程中有各种类型的指针,每种都有不同的用途。

  1. 基本指针
  2. 空指针
  3. 指针的指针(双指针)
  4. 智能指针
  5. void 指针
  6. 整数指针
  7. 浮点数指针
  8. 数组指针
  9. 函数指针
  10. 结构指针(Struct 指针)
  11. 野指针
  12. 悬空指针

1. 基本指针

基本指针或主指针是存储另一个变量内存地址的变量。主指针是另一个变量的内存地址的变量。指针不实际存储数据本身,而是连接到存储数据的内存位置。因此,程序将有机会使用它来读取和写入位于给定内存地址的值。

C++ 中的实现

示例

编译并运行

输出

 
Value of variable: 12
Address of variable: 0x7fff4cbc3074
Value of pointer (Address it holds): 0x7fff4cbc3074
Value pointed to by pointer: 12

C 语言实现

示例

编译并运行

输出

 
Value of variable: 12
Address of variable: 0x7ffd68da3ec4
Value of pointer (holding Address): 0x7ffd68da3ec4
Value pointed to by pointer: 12   

2. 空指针

不指向合法内存位置的指针。它在许多编程语言中使用,其中指针不指向对象,或者可以说它未初始化。

在 Python 中实现

示例

编译并运行

输出

 
The variable is None.   

Java 实现

示例

编译并运行

输出

 
The variable has a null value.   

C++ 中的实现

示例

编译并运行

输出

 
The pointer is null.   

C 语言实现

示例

编译并运行

输出

 
The pointer is null.   

3. 指针的指针(双指针)

指向指针的指针也称为双指针。它是一种存储另一个指针内存地址的指针类型。如果需要,可以使用双指针间接引用数据。

C++ 中的实现

示例

编译并运行

输出

 
10 10 10 
14 14 14 
18 18 18    

4. 智能指针

智能指针充当指针,是封装指针的包装类。这些指针会自动管理它们指向的内存。它确保在不再需要时正确地释放内存。它在 C++ 中可用。

C++ 中的实现

示例

编译并运行

输出

 
uniPtr = 5
shPtr = 10, shPtr2 = 10
shPtr use_count = 2   

5. void 指针

void 指针是没有关联数据类型的指针。它可以存储任何数据类型的地址,并且可以转换为任何类型。

C++ 中的实现

示例

编译并运行

输出

 
Integer value: 2
Float value: 3.04   

C 语言实现

示例

编译并运行

输出

 
Integer value: 2
Float value: 3.040000   

6. 整数指针

顾名思义,整数指针指向存储整数的内存地址。

C++ 中的实现

示例

编译并运行

输出

 
Value of v: 2
Value of p: 0x7ffe76867834
The value pointed by p: 2   

C 语言实现

示例

编译并运行

输出

 
Value of v: 2
Value of p: 0x7ffe76867834
The value pointed by p: 2   

7. 浮点数指针

浮点数指针是指向存储浮点数的内存地址的指针。

C++ 中的实现

示例

编译并运行

输出

 
Value of v: 2.1
Value of p: 0x7ffefcae6e04
The value pointed by p: 2.1  

C 语言实现

示例

编译并运行

输出

 
Value of v: 2.100000
Value of p: 0x7ffe396865b4
The value pointed by p: 2.100000  

8. 数组指针

数组指针是一种可以用于指向数组第一个元素的指针。除了存储相关数组的第一个元素的内存地址外,数组指针还与数组的元素类型相关联。

C++ 中的实现

示例

编译并运行

输出

 
11 21 31 41 51    

C 语言实现

编译并运行

输出

 
11 21 31 41 51   

9. 函数指针

函数指针是存储函数地址的指针。使用函数指针,可以调用函数指针指向的函数。这是因为函数指针具有函数地址。

C++ 中的实现

示例

编译并运行

输出

 
In the function foo.  

C 语言实现

示例

编译并运行

输出

 
In the function foo1.
In the function foo2.   

10. 结构指针(Struct 指针)

结构指针是存储结构地址的指针。结构指针对于动态内存分配是必需的。函数参数以较小的开销传递给大型结构。

C++ 中的实现

示例

编译并运行

输出

 
Employee ID: 12
Employee Name: Tpointtech

C 语言实现

示例

编译并运行

输出

 
Employee ID: 12
Employee Name: Tpointtech

11. 野指针

这是一种不存储任何变量地址的指针。

如何避免野指针?

  • 声明时初始化指针。
  • 将未使用的指针设置为 null。
  • 小心使用动态内存分配。

C 语言实现

输出

 
Runtime Error:
run: line 1:     3 Segmentation fault      (core dumped) ./a.out   

C++ 中的实现

输出

 
Runtime Error:
run: line 1:     3 Segmentation fault      (core dumped) LD_LIBRARY_PATH=/usr/local/gcc-9.2.0/lib64 ./a.out  

12. 悬空指针

悬空指针是指在内存被释放或超出作用域后仍然引用内存位置的指针。访问此类指针会导致未定义的行为——从垃圾值到程序崩溃。

悬空指针的原因

  • 内存释放
  • 局部变量的返回地址
  • 变量超出作用域

如何避免悬空指针?

  • 将已释放的指针设置为 null
  • 避免返回局部变量的地址
  • 如果需要,请使用静态变量

C++ 中的实现

示例

编译并运行

输出

 
0  

C 语言实现

示例

编译并运行

输出

 
Error executing code.  

注意:Python、Java 和 JavaScript 等语言不直接支持指针,但它们使用对象和引用等类似概念。

指针的应用

指针由于各种原因至关重要

  • 动态内存分配:在 C++ 和 C 等语言中,堆上的动态内存分配是通过指针完成的。这对于创建图、树和链表等数据结构是必需的。
  • 效率:指针可以提高代码效率。通过将指针传递给函数,可以避免复制大型数据。
  • 函数指针:指针甚至可以指向函数。这允许动态函数回调和调用。

指针的优点

  • 效率:指针可以直接访问内存位置,从而实现高效的内存管理。
  • 动态内存分配:指针允许动态内存分配。因此,可以在程序运行时根据需要分配内存。
  • 传递参数:指针允许函数通过发送内存地址而不是值来更改其作用域之外的变量。这对于需要更改变量或返回多个值的函数很有用。
  • 减少数据冗余:与复制大型数据相比,指针可以指向内存中的相同数据。因此,避免了冗余并节省了内存。

指针的缺点

  • 复杂性:引入指针会增加复杂性,还会使代码更难维护和理解。
  • 内存管理:在 C++ 和 C 等语言中,处理指针需要手动进行内存管理。这可能导致分段错误或内存泄漏(未释放的内存)。
  • 安全漏洞:我们知道指针可以直接访问内存地址。因此,如果处理不当,可能会在程序中引入安全问题。它还可能导致程序崩溃。

重要提示

  • 指针算术:指针可以使用四种算术运算符:++、--、+ 和 -。
  • 指针数组:我们可以定义数组来存储多个指针。
  • 指向指针的指针:C 允许我们拥有指向指针的指针,依此类推。
  • 将指针传递给 C 中的函数:通过引用或通过地址传递参数,可以使被调用函数更改调用函数中的传递参数。
  • 从 C 中的函数返回指针:C 允许函数返回指向局部变量、静态变量和动态分配的内存的指针。

结论

编程中的指针是有用的工具,它们存储变量的内存地址。不同类型的指针,如 Null、Void 等,具有独特的用途和属性。它们在内存管理中起着重要作用,使程序更灵活、更高效。另一方面,使用指针也有风险,因为不正确的使用可能导致崩溃和错误。

指针选择题

1. 存储另一个变量地址的变量称为_____________。

  1. 瞬态变量
  2. 静态变量
  3. 指针
 

答案:c)

解释:存储另一个变量地址的变量称为指针。


2. 在 C 和 C++ 等语言中,堆上的动态内存分配是通过 _____________ 完成的。

  1. Class
  2. 变量
  3. 函数
  4. 指针
 

答案:c)

解释:在 C 和 C++ 等语言中,指针负责进行动态内存分配。

数组指针


3. _____________ 是一个没有关联数据类型的指针。

  1. 整数指针
  2. 数组指针
  3. 浮点数指针
  4. void 指针
 

答案: d)

解释:void 指针是一个没有关联数据类型的指针。


4. _____________ 充当指针,是封装指针的包装类。

  1. 智能指针
  2. 函数指针
  3. 浮点数指针
  4. 结构指针
 

答案: a)

解释:智能指针充当指针,是封装指针的包装类。


5. _____________ 指针不指向合法的内存位置。

  1. void 指针
  2. 空指针
  3. 浮点数指针
  4. 结构指针
 

答案: b)

解释:不指向合法内存位置的指针。它在许多编程语言中使用,其中指针不指向对象,或者可以说它未初始化。