侧边栏壁纸
博主头像
DevWiki博主等级

不怕慢,只怕站!

  • 累计撰写 156 篇文章
  • 累计创建 90 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录
C++

引用、指针、句柄有什么区别?

DevWiki
2023-05-06 / 0 评论 / 0 点赞 / 20 阅读 / 7732 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2024-03-30,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

内存模型:堆与栈

在学习 Java 和 C# 语言过程中, 我们都知道这两种语言都属于托管开发模式, 即:

在运行时由一个管理器(或运行时环境)负责管理应用程序的生命周期、内存分配、资源管理等任务,从而提供更高级别的抽象和便利的开发体验。

在这种的托管模式中, 内存可分为两类: Stack 和 Heap

  • Stack : 称为栈, 栈是程序运行时自动分配和释放的一块内存区域,用于存储局部变量、方法参数、返回地址和临时数据等。栈内存的管理是由程序运行时系统自动完成的,因此无需手动申请或释放。每个线程都有一个独立的栈,栈的大小是固定的。当栈空间不足时,会抛出 StackOverflowException 异常。
  • Heap : 称为堆, 堆是程序运行时动态分配和释放的一块内存区域,用于存储对象和数组等。堆内存的管理是由程序员手动申请和释放的,需要使用 new 运算符来创建对象。堆内存的大小是不固定的,当堆空间不足时,程序会自动进行垃圾回收(GC)。

在Java 和 C# 语言中,我们将数据类型分为: 基本类型 和引用类型。基本类型一般包括值类型,在内存中存储在栈中。

引用类型在内存中分为 引用 和 实例化的对象,引用存放在栈中,实例化的对象在堆中。

存储示意图如下:

什么是引用?

在 Java 和 C# 中,引用(Reference)存储的是对象的内存地址。当我们创建一个对象时,会在堆上为该对象分配一块连续的内存空间,并返回这块内存空间的地址。当我们创建一个引用时,实际上是在栈上创建了一个变量,这个变量存储了对象在堆上的地址。

例如,在上面的图中代码创建了一个对象和一个引用:

MyClass cls1 = new MyClass();

在这个例子中,new MyClass() 会在堆上分配一块内存空间来存储一个 MyClass 对象,并返回这个对象的内存地址。然后,会将这个内存地址赋值给 cls1 变量,即 cls1 存储了对象在堆上的地址。

当我们使用引用访问对象的属性或方法时,实际上是通过引用存储的对象地址来访问对象的内存空间。例如,下面的代码使用引用访问对象的属性:

cls1.toString();

在这个例子中,cls1.toString() 实际上是通过引用 cls1 存储的对象地址来访问对象的 toString() 方法。

需要注意的是,Java 和 C# 中的引用是一种动态的概念,它可以随时指向不同的对象。例如,下面的代码将一个新的对象赋值给 obj 变量:

cls1 = new MyClass();

在这个例子中,cls1 变量将引用一个新的对象,而之前的 MyClass 对象则成为了孤岛对象,可能会被垃圾回收器回收。

总之,在 Java 和 C# 中,引用存储的是对象的内存地址,它可以随时指向不同的对象,并通过这个地址来访问对象的内存空间。

在C++ 中, 我们可以如下定义一个变量:

int a = 0;

此时 a 是一个变量, 而引用(Reference)是一种别名,它是一个已存在对象的别名,可以用来访问该对象。使用下面的方式定义一个引用:

int& b = a;

在 C++ 中,引用是一个别名,它允许使用一个变量的别名来访问该变量。引用使用 & 符号来声明,并且必须在定义时初始化。

引用有以下几个特点:

  1. 引用是一个别名,引用变量的值与原变量的值是一样的。
  2. 引用必须在定义时初始化,一旦初始化后,就不能被重新赋值为另一个变量,只能作为原变量的一个别名来使用。
  3. 引用在内存中不占用独立的存储空间,只是原变量的另一个名字。

如何理解上述的第2条呢, 看代码:

int main() {
    int a = 1;
    int& ref = a;
    cout << "变量a的值为:" << a << endl;
    cout << "引用ref的值为:" << ref << endl;
    int b = 10;
    ref = b;
    b = 3;
    cout << "变量a的值为:" << a << endl;
    cout << "引用ref的值为:" << ref << endl;
    cout << "变量b的值为:" << b << endl;
    return 0;
}

上述代码中:

  1. 声明变量 a 并赋值为 1
  2. 创建变量 a 的引用 ref
  3. 输出此时的 a 和 ref 的值
  4. 声明变量 b 并赋值为 10
  5. ref = b 意为把 b 的值赋值为 ref,即赋值给 a
  6. 赋值 b 的值为3
  7. 输出 a ref b 的值

结果如下:

变量a的值为:1
引用ref的值为:1
变量a的值为:10
引用ref的值为:10
变量b的值为:3

特别的, 如果引用指向的对象被销毁或改变了地址,那么访问该引用就会出现未定义的行为,可能会导致程序崩溃或其他问题。因此,在使用引用时需要格外注意引用的生命周期和作用域,以避免出现悬垂引用等问题。

什么是指针?

在 C++ 中,指针(Pointer)是一种特殊的变量,它存储了一个内存地址,指向另一个变量或对象。指针可以用来访问和修改指向的变量或对象,它是 C++ 中最重要的概念之一。

指针在 C++ 中的定义方式为:类型* 变量名,其中 类型 是指针指向的变量或对象的类型,变量名 是指针变量的名称。例如,下面的代码定义了一个指向整数类型的指针:

int* ptr;

在这个例子中,ptr 是一个指向整数类型的指针变量,它存储了一个整数类型变量的地址。即 ptr 只能存储整数类型变量的地址, 下图中 ptr 指向变量 a 的地址可以, 指向 str 的地址会提示编译错误.

image-20230504235710785

通过指针访问对象或者变量,称为 解引用,

未初始化的指针称为 野指针,

初始化为 nullptr 的指针称为 空指针,

如:

int* ptr1; // 野指针
int* ptr2 = nullptr; // 空指针
int a = 10;
int* ptr = &a;
cout << "a :" << *p << endl; // 解引用

指针在 C++ 中有许多重要的用途,例如:

  • 动态内存分配:通过 new 运算符可以动态地分配内存空间,并返回指向该内存空间的指针。通过 delete 运算符可以释放已经分配的内存空间。
  • 函数参数传递:可以通过指针将数据从一个函数传递到另一个函数,或者通过指针返回函数的结果值。
  • 数组访问:可以通过指针访问数组中的元素。
  • 对象访问:可以通过指针访问对象的属性和方法。

需要注意的是,在使用指针时需要格外小心,因为指针可以指向任何地址,包括未初始化的地址、野指针和已经释放的内存地址等,这些都可能导致程序出错或崩溃。因此,在使用指针时需要格外注意指针的生命周期和作用域,以避免出现悬垂指针、空指针等问题。

可以给指针定义引用么?

答案是: 可以,语法如下:

int* ptr = new int(10);   // 创建一个指向整型变量的指针,其值为 10
int*& ref = ptr;          // 定义一个指针的引用,它引用指针 ptr

在这个例子中,我们首先创建了一个指向整型变量的指针 ptr,并将其初始化为一个新的整型变量,它的值为 10。然后,我们定义了一个指针的引用 ref,它引用了指针 ptr。此时,refptr 指向同一个内存地址,它们实际上是同一个变量的不同名称。因此,对 ref 的修改会影响到指针 ptr 的值,反之亦然。

指针的引用在 C++ 中通常用于函数参数传递或返回值。通过使用指针的引用,我们可以避免指针拷贝和内存分配的开销,提高程序的性能。同时,使用指针的引用还可以让代码更加简洁和易读。

C++中指针和引用有什么区别?

在 C++ 中,指针和引用都是用来访问内存中的数据的。它们之间的主要区别是:

  1. 指针可以被重新赋值为指向其他的内存地址,而引用一旦初始化后就不能被重新赋值。也就是说,指针是可以改变其指向的对象,而引用始终指向其初始化时所绑定的对象。
  2. 指针可以为NULL或nullptr,表示指向一个空地址,而引用必须始终引用一个有效的对象。因此,使用引用时需要注意避免引用空指针或未初始化的变量。
  3. 对指针进行解引用操作时,需要先判断指针是否为NULL或nullptr,否则可能会导致运行时错误。而引用不需要解引用操作,因为它始终引用一个有效的对象
  4. 指针可以进行算术运算,如加、减等操作,以及使用下标访问数组中的元素。而引用不支持这些操作,因为它只是绑定到一个对象上的别名,没有实际的地址和大小。
  5. 指针可以作为函数参数传递,以及动态分配内存等高级应用。而引用通常用于函数参数传递,因为它可以避免对象被复制,提高程序的效率。

什么是句柄?

句柄(Handle)是一种用于标识某个资源或对象的唯一标识符。在 Windows 操作系统中,句柄通常用于标识系统分配的资源,例如窗口、设备上下文、文件、进程等。

句柄本质上是一个整数值或指针类型,它与资源实体之间的映射关系由操作系统维护。在使用句柄时,可以通过句柄来操作相应的资源或对象,例如读写文件、操作窗口、控制进程等。

Windows 操作系统中有很多种句柄类型,包括窗口句柄、设备上下文句柄、文件句柄、进程句柄等。每种句柄类型都有其特定的用途和操作方法。

在 Windows API 中,可以使用一些函数来获取和操作句柄,例如 FindWindow() 函数用于查找窗口句柄,CreateFile() 函数用于创建文件句柄,OpenProcess() 函数用于打开进程句柄等。这些函数通常返回一个句柄,用于标识相应的资源或对象。

句柄和指针有什么区别?

句柄(Handle)是一个 Windows 操作系统中的概念,而不是 C++ 语言本身的概念。

句柄和指针都可以用来表示一个对象或变量的内存地址,但它们的用途和含义有所不同。

如前面所说:

  • 句柄是一种用于标识某个资源或对象的唯一标识符, 只能通过系统函数来访问和操作。
  • 指针是一个用于存储变量或对象内存地址的数据类型,它直接指向一个对象或变量的内存地址。

后记

如果你喜欢作者的内容, 请关注我的微信公众号:

或者加入微信群, 一起交流学习, 如二维码过期,请在公众号回复"微信群", 获取最新二维码或者添加我的微信: dev-wiki.

image-20230505230134025
0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区