C# 可以说对指针的操作和使用非常少,这一块指针的使用一直对我来说是一个盲点。它不像C语言和C++ 等对指针的操作非常多。会用,首先得明白其原理和作用,然后实践。在学习和了解一个新的知识点的时候我一直是遵循这个步骤。首先一定得知其然,然后知其所以然。要先明白其概念和原理,因为概念和原理这个东西是抽象的,所以必须要加以实践,通过实践才能完全理解,这个时候达到一个会用的阶段。这个阶段是对这个新知识点的会用而已,要想学得通透还需要再回去反复琢磨概念和原理。这样才能上升到一个新的高度。通过反复使用,然后从中获取到一些新的东西,举一反三,触类旁通。这样才能算是真正的掌握了。学习技能是这样,不必求快,如果只是停留在初步会用的阶段,因为知道的少而没有考虑到其它情况,这样设计出来的软件必然Bug丛生。
先初步了解概念的东西(概念原理)—通过概念的东西练习----练习达到熟练的地(实践练习)步—再回去反复琢磨概念原理—(总结归纳)拓展和延伸其它类似的东西像滚雪球一样滚出和它相关联的东西— 再其它地方使用多练习实践----考虑这个东西将来可以用在哪些地方(积累案列)—以不至于脑子呆呆空空如也— 然后创造延伸属于自己的东西(融汇贯通)。
1 指针概念原理
1.1 指针的概念
什么是指针呢? 脑子里面蹦出来很多关于C语言的很多东西。指针就是内存地址的一个变量,它的值是其它变量的地址。指针存储了数据在内存中的位置信息。通过指针可以间接访问和操作存储在该位置的数据。
1.2 指针的作用
-
直接访问内存。
指针提供了一种直接操作内存的方式,可以在内存中灵活地存储和访问数据。通过指针可以直接访问内存中的特定位置,读取或修改存储在该位置的数据。这对于一些需要高效处理数据或与底层硬件进行交互的程序非常有用。 -
实现数据结构
许多数据结构,如链表,树,图等,都依赖于指针来实现。指针可以用来建立数据元素之间的链接关系,使得数据能够以动态的方式组织和存储。例如,在链表中,每个节点包含一个数据元素和一个指向下一个节点的指针,通过指针的链接,可以方便地遍历和操作链表中的数据。 -
函数参数传递
指针可以作为函数的参数传递,使得函数能够直接修改调用者传递的变量的值。这种方式需要在函数内部修改外部变量的情况下非常有用,避免了通过函数返回值来传递修改后的值的麻烦,同时也提高了程序的效率。
- 与底层系统交互
与底层系统交互时,指针常常是必不可少的,例如,在操作系统的系统调用中,许多参数都是通过指针传递的,以便操作系统能够直接访问和修改用户程序中的数据。此外,在与硬件设备进行通讯时,指针也可以用来指定数据的存储位置和传输方向。
1.3 指针的使用方式
-
声明指针变量
声明指针变量:在 C 或 C++ 中,声明指针变量的语法为 类型 *指针变量名。例如,int *ptr; 声明了一个名为 ptr 的指针变量,它可以指向一个 int 类型的变量。指针变量本身也有自己的地址和占用一定的内存空间,其大小通常取决于操作系统和编译器的实现,但一般在 32 位系统中为 4 字节,在 64 位系统中为 8 字节。
获取变量地址:使用取地址运算符 & 可以获取一个变量的地址。例如,int num = 10; int *ptr = # 将变量 num 的地址赋给了指针变量 ptr,此时 ptr 就指向了 num。 -
指针 的解引用
指针解引用:通过指针解引用运算符 * 可以访问指针所指向的变量的值。例如,在上述代码中,*ptr 的值就是变量 num 的值,即 10。同时,也可以通过指针解引用对所指向的变量进行赋值操作,如 *ptr = 20; 将把变量 num 的值修改为 20。 -
指针运算
指针运算:指针可以进行一些算术运算,如加法、减法等。指针运算的结果是根据指针所指向的数据类型的大小来计算的。例如,对于一个指向 int 类型的指针 ptr,ptr++ 将使指针指向下一个 int 类型的位置,即地址增加 sizeof(int) 字节。指针运算在遍历数组、操作链表等数据结构时非常有用。
1.4 指针的风险和注意事项
- 悬空指针: 当指针所指向的内存已经被释放掉了,但指针仍然指向该内存的地址时,就会产生悬空指针,无法访问到数据。
- 内存泄露: 如果动态分配了内存,但在使用完毕后没有及时释放,就会导致内存泄露。内存泄露会导致程序占用的内存不断增加,最终可能耗尽系统资源。在使用指针动态分配内存时,一定要注意及时释放内存,避免内存泄露的发生。
- 指针越界,指针越界是指指针的访问超出其所指向的内存区域的范围。这可能会导致程序访问到其它变量或内存区域,从而引发数据损坏或程序错误。在使用指针时,要确保指针的操作在合法的内存范围内进行,避免指针越界。
- 类型不匹配:指针的类型必须与它所指向的变量的类型相匹配,否则可能会导致数据访问错误。例如,如果一个指针被声明为指向 Int 类型,但却被用来指向一个Char类型的变量,那么在解引用指针时就会得到错误的数据。
指针是一种强大的但又具有一定风险的编程工具,在使用指针时,需要对内存管理和指针操作有深入的理解,遵循良好的编程习惯和安全规则,以确保程序的正确性和稳定性。
2 句柄的概念
在计算机编程中,句柄(Handle)是一个用于标识和引用系统资源或对象的抽象概念,它是一个整数值或类似的数据结构,操作系统或程序通过它来操作和管理各种资源,以下是关于句柄的详细介绍:
2.1. 句柄的作用
资源标识与管理:句柄的主要作用是作为一种间接引用的方式来标识和操作系统中的各种资源,如文件、窗口、进程、线程、设备等。通过句柄,操作系统可以在内部维护资源的相关信息和状态,并提供统一的接口来让应用程序对这些资源进行访问和操作,而不需要应用程序直接了解资源在内存中的具体位置和细节。
资源共享与保护:句柄还可以用于实现资源的共享和保护机制。多个应用程序或不同的代码模块可以通过获取同一个资源的句柄来共享对该资源的访问,但操作系统可以根据句柄的权限设置来控制对资源的访问方式和范围,从而确保资源的安全性和一致性。
2.2. 句柄的类型
- 文件句柄:
用于标识和操作文件资源。当应用程序打开一个文件时,操作系统会为该文件分配一个唯一的文件句柄,应用程序通过这个句柄来进行文件的读写、定位等操作。不同的操作系统和编程语言对文件句柄的具体表示和操作方式可能会有所不同,但基本的概念都是通过一个句柄值来代表打开的文件,以便进行后续的文件 I/O 操作。 - 窗口句柄:
在图形用户界面(GUI)编程中,窗口句柄用于标识和操作窗口对象。每个窗口都有一个唯一的句柄,通过这个句柄,应用程序可以控制窗口的显示、隐藏、移动、大小调整等属性,以及接收和处理与窗口相关的消息和事件,如鼠标点击、键盘输入等。 - 进程句柄和线程句柄:用于标识和管理进程和线程资源。当创建一个新的进程或线程时,操作系统会为其分配相应的句柄,通过这些句柄,应用程序可以对进程和线程进行控制和管理,如启动、暂停、终止进程或线程,获取进程或线程的状态信息等。
- 设备句柄:
用于与各种外部设备进行交互,如打印机、显示器、键盘、鼠标等。应用程序通过获取设备的句柄,可以向设备发送控制命令、读取设备的状态信息或进行数据传输等操作,实现与外部设备的通信和协同工作。
2.3. 句柄的使用方式
1 获取句柄:
应用程序通常通过调用特定的系统函数或 API 来获取资源的句柄。例如,在 Windows 操作系统中,使用 CreateFile 函数可以打开一个文件并获取其文件句柄,使用 FindWindow 函数可以根据窗口的类名或标题查找并获取窗口句柄等。不同的资源类型有各自对应的获取句柄的函数或方法,这些函数在成功获取句柄后会返回一个表示该句柄的值,应用程序后续就可以使用这个句柄来操作相应的资源。
2. 使用句柄进行操作:
一旦获取了句柄,应用程序就可以将其作为参数传递给其他相关的系统函数或 API 来对资源进行各种操作。例如,使用文件句柄作为参数调用 ReadFile 函数可以从文件中读取数据,使用窗口句柄作为参数调用 ShowWindow 函数可以显示或隐藏指定的窗口等。通过句柄进行操作时,应用程序不需要关心资源在内存中的具体位置和实现细节,只需要按照操作系统提供的接口和规则来使用句柄即可。
3. 释放句柄:
当应用程序不再需要使用某个资源时,应该及时释放其对应的句柄,以通知操作系统可以回收相关的资源。释放句柄的操作也是通过调用特定的系统函数来完成的,如在 Windows 中使用 CloseHandle 函数来关闭文件句柄、窗口句柄、进程句柄等各种类型的句柄。如果不及时释放句柄,可能会导致资源泄漏,浪费系统资源并可能影响其他应用程序对资源的正常使用。
3. 句柄与指针的区别
3.1 概念层面
句柄是一种更高级别的抽象概念,它是对系统资源或对象的一种标识和引用,不直接涉及到内存地址和数据结构的具体细节。而指针则是直接指向内存中的某个数据存储位置,通过指针可以直接访问和操作内存中的数据。
3.2 操作层面
使用句柄进行操作时,通常是通过调用系统提供的函数或 API,并将句柄作为参数传递给这些函数来完成对资源的操作,操作的具体细节由操作系统或底层库来处理。而指针操作则更加直接和底层,开发人员可以通过指针算术运算、解引用等操作来直接访问和修改内存中的数据,需要对内存管理和数据结构有更深入的了解和控制。