什么是指针

指针(Pointer):指针是一个变量类型,它存储的是内存地址,而不是数据值本身。指针变量用于指向内存中的某个位置,通过指针可以间接地访问或修改该位置的数据。在CSP中理解指针的基本概念即可。

内存的本质

计算机的内存(memory)是一块用于存储数据的空间,由一系列连续的存储单元(bit/Byte)组成。

由于1个bit只能表示两个状态,因为我们将每8个bit分为一组,命名为Byte,并将Byte做为内存的最小单元,为了区分每个存储单元,给每个Byte一个唯一的编号(这个编号就叫内存单元的地址)

变量的本质

当在代码中定义一个变量,例如int a=10时,实际发生的动作时程序向内存申请了一块空间(对int来说是4个单元)。然后依次将每个字节的数据写入到对应的内存单元中。

定义变量之后,变量名称可以直接表示变量的值,如果想要获得变量的地址,可以通过取地址运算符&来实现

int a=10;
cout<<&a<<endl; //输出变量a所占内存空间(4个byte)的起始地址,&为取地址符

输出结果大概会是像 0x73fe4c 这样的一串(十六进制)数字,每次运行的结果可能都不一样,因为计算机给变量分配的内存空间是随机的,这个数字就是变量a的起始地址编号,即int类型的4个字节中,编号最小的那一个。在C++中,以第一个字节的地址(起始地址或首地址)编号作为整个变量的地址。

指针的定义和使用

  • 创建指针变量:int *p;

    指针变量的定义跟普通数值型变量的定义并没有本质区别,只是需要在每个变量名前加一个*修饰

  • 间接取值:cout << *p;

    通过取内容运算符(*,也称解引用),可以把指针指向的内存中的数据取出来。

#include<bits/stdc++.h>
using namespace std;
int main() {
    //定义整数变量x,存储值为10 
    int x=10;
    //&:表示获取变量x的地址 
    //定义整型指针,存储整型变量x的地址 
    int *p=&x;
    //p是指针变量(int*) 
    //*p不是地址,是p这个地址指向的值
    cout<<p<<" "<<*p<<endl;
    //通过指针修改其指向的值 
    *p=*p+1;
    cout<<x<<endl;
    return 0;
}

[info] 注意

创建指针变量时的*和创建完指针变量间接取值时的*是完全不同的概念。

图解指针

创建 a、b 变量,并在内存中分配空间。

创建指向指针变量 p1、p2 并在内存中分配空间。

将 p1 指向 a,即指针变量 p1 中保存 普通变量 a 的地址。

函数参数的传递

值传递

void swap1(int x, int y) /* 定义中的x,y变量被称为swap1函数的形式参数 */ 
{ 
   int tmp; 
   tmp = x; 
   x = y; 
   y = tmp; 
  printf("x = %d, y = %d \n", x, y); 
}
int main() {
    int a = 4,b = 6; 
    swap1(a, b); /*a,b 变量为 swap1 函数的实际参数。*/ 
    printf("a = %d, b = %d \n", a, b); 
    return 0; 
}
//输出结果
x = 6, y = 4
a = 4, b = 6

函数只是把 a、b 的 值通过拷贝赋值传递给了 x、y,函数里头操作的只是 x、y 的值并不是 a、b 的值。 这就是所谓的参数的值传递了

引用传递

void swap2(int &x, int &y) /* 定义的形式参数的格式与值传递不同 */ 
{ 
     int tmp = x; 
     x = y; 
     y = tmp; 
     printf("x = %d, y = %d \n", x, y); 
} 
int main() {
    int a = 4,b = 6; 
    swap2(a, b); /*注意:这里调用方式与值传递一样*/
    printf("a = %d, b = %d \n", a, b); 
    return 0; 
}

函数形参x、y 前都有一个取地址符号“&”。有 了这个,调用 swap2 时函数会将 a、b 分别代替了 x、y 了,我们称:x、y 分别引用了 a、b 变量。这样函数里头操作的其实就是实参 a、b 本身,也 是说函数里是可以直接修改到 a、b 的值

地址传递

void swap3(int *x, int *y)  
{ 
   int tmp; 
   tmp = *x; 
   *x = *y; 
   *y = tmp; 
   printf("x = %d, y = %d \n", *x, *y); 
}
int main() {
    int a = 4,b = 6; 
    swap3(&a, &b); 
    printf("a = %d, b = %d \n", a, b); 
    return 0; 
}
//输出结果
x = 6, y = 4
a = 6, b = 4

指针 x、y 的值已经分别是 a、b 变量的地址值了。接下来,对*x、*y 的操作就是对 a、b 变量本身的操作了。所以函数里头的交换就是对 a、b 值的交换了, 这就是所谓的地址传递。

指针访问数组

什么时候使用指针

  • 动态内存分配 使用 newdelete 运算符进行动态内存分配和释放。
int *array = new int[10]; // 分配一个大小为 10 的 int 数组 
delete[] array;           // 释放分配的内存
  • 实现复杂数据结构 在实现链表、树、图等动态数据结构时,指针是必要的。

  • 函数参数传递 当需要在函数内部修改传入的参数,或者传递大型对象(避免拷贝开销)时,可以使用指针。

    void modifyValue(int* ptr) {     *ptr = 100; }
    
  • 数组和指针运算 指针可以用于遍历数组、进行指针算术运算等。

      int arr[5] = {1, 2, 3, 4, 5}; 
      int* ptr = arr; 
      for (int i = 0; i < 5; ++i) {     
          cout << *(ptr + i) << " "; 
      }
    

为什么要使用指针?

  • 灵活性 指针提供了对内存的直接访问权限,可以高效地管理和操作内存。-
  • 动态性 可以在运行时动态地分配和释放内存,适应程序的需要。
  • 高效性 通过指针传递参数,避免了大对象的拷贝,提高了程序的性能。

什么是引用

引用是已有变量的别名,它是已存在变量的另一个名字。一旦将引用初始化为某个变量,就无法再改变其指向。

引用的声明和使用

int a = 10; 
int &ref = a; // ref 是变量 a 的引用 
ref = 20;  // 现在 a 的值也变为 20 
cout << a; // 输出 20

什么时候使用引用?

  • 函数参数传递 当希望在函数内部修改参数的值,或者传递大型对象时,使用引用。
void modifyValue(int& ref) {     
    ref = 100; 
}
  • 范围 for 循环 使用引用避免拷贝,提高效率。
for (auto& item : container) {     // 修改 item }

为什么要使用引用?

  • 安全性 引用必须在声明时初始化,不能为 nullptr,降低了空指针引发错误的风险。
  • 简洁性 语法上更简洁,使用起来像普通变量,无需解引用。
  • 效率 与指针相比,引用的使用减少了指针解引用的开销(在许多情况下,编译器会优化)。
  • 易读性 代码更加清晰,容易理解传递的是引用关系。

指针和引用的区别

特性 指针 引用
是否可为空 可以为空(nullptr) 不能为空,必须绑定到有效对象
初始化后是否可变 可以重新指向其他对象 绑定后不可更改
语法 使用 *、->、& 等运算符 使用与普通变量相同的语法
是否需要解引用 需要使用 * 运算符解引用 自动解引用,直接使用
常量性 可以有 const 指针,指向常量或非常量 可以有对常量的引用
内存地址获取 使用指针本身即可表示地址 使用 & 获取引用对象的地址

如何选择使用指针还是引用?

1. 使用引用的场合

  • 参数传递 当函数需要修改参数值,或者避免拷贝大对象时。
void process(const MyClass &obj);

2. 使用指针的场合

  • 需要空值 当可能不存在对象,需要表示空(nullptr)的情况。
  • 动态内存管理 需要动态分配和释放内存时。
  • 复杂数据结构 实现链表、树等需要链接节点的结构。

总结

  • 指针

    • 优势:灵活、功能强大,可为空、可动态管理内存。
    • 劣势:使用不当可能导致内存泄漏、悬空指针、难以调试。
  • 引用

    • 优势:语法简洁、安全,避免空指针错误。
    • 劣势:绑定后不可更改,不能表示空值。
  • 选择指南

    • 优先使用引用:当不需要表示空值,且不需要重新绑定时。
    • 使用指针:当需要动态管理内存、处理空值、重新指向不同对象时。

引用安全少烦恼,指针灵活需防飘。

性能优化先引用,动态结构指针瞧。

Copyright ©图灵之星 2024,转载需注明出处该文件修订时间: 2025-04-05 13:49:25

results matching ""

    No results matching ""