进一步认识指针
使用指针的好处
- 可以动态分配内存,由于可以直接操作内存,所以运行效率更高
- 能写出简洁、高效的代码
- 为动态数据结构,尤其是树和链表,提供支持
- 可以写出更加复杂、灵活的数据结构
- 高效地按引用“复制”数值与结构,特别是作为函数参数的时候,可以按照引用传递函数参数,提高开发效率
指针与引用
程序设计中的引用其实就是别名的意思,它用于定义一个变量来共享另一个变量的内存空间,进而提高程序的开发效率。指针指向另一个内存空间的变量,可以通过它来索引另一个内存空间的内容,而指针本身也有自己的内存空间
引用与指针有着相同的地方,即指针指向一块内存,它的内容是所指内存的地址,引用是某块内存的别名。但是,它们之间也存在着差别,具体为:
-
从本质上讲,指针是存放变量地址的一个变量,在逻辑上独立,它可以被改变,即指向的地址可以被改变,其指向的地址中存放的数据也可以被改变。而引用则只是一个别名而已,它在逻辑上不是独立的,它的存在具有依附性,所以引用在一开始就被初始化,而且引用的对象在其整个生命周期中是不能被改变的,即自始至终只能依附于同一个变量,具有“从一而终”的特性
-
当作为参数传递时,两者不同。在C++中,指针域引用都可以用于函数的参数传递,指针传递参数本质上是值传递的方式,它所传递的是一个地址值。而在引用传递过程中,被调函数的形参虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。任何对于引用参数的处理都会通过一个间接寻址的方式操作到主调函数中的相关变量。而对于指针传递的参数,如果改变被调函数中的指针地址,它将影响不到主调函数的相关变量。
-
引用使用时不需要解引用(*),而指针需要解引用
-
引用不可以为空,而指针可以为空。引用必须与存储单元,一个引用对应一个存储单元
-
对引用进行sizeof操作得到的是所指向的变量(对象)的大小,而对指针进行sizeof操作得到的是指针本身(所指向的变量或对象的地址)的大小,
typeid(T) == typeid(T&)
恒为真,sizeof(T) == sizeof(T&)
恒为真。但是当引用作为成员时,其占用空间与指针相同 -
如果返回动态分配的对象或内存,必须使用指针,引用可能会引起内存泄漏
指针与数组
指针可以随时指向任意类型的内存块,而数组可以在静态存储区被创建。从原理与定义上看,虽然指针与数组是不同的概念,但指针却可以方便地访问数组或者模拟数组,两者存在着一种貌似等价的关系,但也存在诸多不同之处:
-
修改内容不同
例如,char a[] = "hello";
,可以通过取下标的方式对其元素值进行修改。例如,a[0]='X';
是正确的,而对于char *p = "world";
,此时p指向常量字符串,所以p[0] = 'X';
是不被允许的 -
所占字节数不同
例如,char *p = "world";
,p为指针,则sizeof(p)
得到的是一个指针变量的字节数,而不是p所指的内存容量。C/C++语言没有办法知道指针所指的内存容量,除非在申请内存时标记出来。但对于数组,可以通过sizeof操作符来计算数组的大小
指针是否可以进行>、<、>=、<=、==运算
可以。由于指针的值是变量的地址,从本质上讲,它就是表示内存地址的一个长整数。既然是整数,当然可以进行上面的所有运算
野指针
野指针是指指向不可用内存的指针。一般来讲,主要有3种情况会出现野指针:
-
任何指针变量在被创建时,不会自动称为NULL指针(空指针),其默认值是随机的,所以指针变量在创建的同时应当被初始化,或者将指针设置为NULL,或者让它们指向合法的内存,否则就会成为野指针
-
当指针被释放(free或delete)后,未能将其设置为NULL,也会导致该指针变为野指针。虽然free和delete把指针所指的内存给释放掉了,但它们并没有把指针本身释放掉,所以建议在调用free或delete后,把指针变量设置为NULL,避免出现野指针
-
指针操作范围超越了变量的作用范围。例如,返回栈内存的指针,当函数调用结束后栈内存就会被释放,此时返回的指针也就成了野指针
空指针
空指针是一个特殊的指针,也是唯一一个对任何指针类型都合法的指针。指针变量具有空指针,表示它当时处于闲置状态,没有指向有意义的内容。为了提高程序的可读性,标准库定义了一个与0等价的符号常量NULL,对于指针p而言,程序里可以写p = 0
或者p = NULL
,这两种写法都把p置为空指针值。C语言保证这个值不会是任何对象的地址。
通用指针
通用指针可以指向任何类型的变量。通用指针的类型用(void *)
表示,因此也称为void指针