1.指针与引用
1.1指针和引用的区别
1.指针有自己的一块空间,而引用只是一个别名;
2.使用sizeof看一个指针的大小是4或8,而引用则是被引用对象的大小;
3.指针可以被初始化为NULL,而引用必须被初始化且必须是一个已有对象的引用;
4.作为参数传递时,指针需要被解引用才可以对对象进行操作,而直接对引用的修改都会改变引用所指向的对象;
5.可以有const指针,但是没有const引用;
6.指针在使用中可以指向其它对象,但是引用只能是一个对象的引用,不能被改变;
7.指针可以有多级指针(**p),而引用只有一级;
8.指针和引用使用++运算符的意义不一样;
9.如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄露。
1 |
|
2.引用
2.1引用概念
引用(reference)为对象起了另外一个名字,引用类型引用(refers to)另外一种类型。通过将声明符写成&d的形式来定义引用类型,其中d是声明的变量名:
1 | int ival = 1024; |
一般在初始化话变量时,初始值会被拷贝到新建的对象中。然而定义引用时,程序吧引用和它的初始值绑定(bind)在一起,而不是将初始值拷贝给引用。一旦初始化完成,引用将和它的初始值对象一直绑定在一起。因为无法令引用重新绑定到另外一个对象,因此引用必须初始化。
引用即别名
引用并非对象,相反的,它只是为一个已经存在的对象所起的另外一个名字。
定义一个引用之后,对其进行的所有操作都是在与之绑定的对象上进行的:
1 | refVal = 2; // 把2赋给refVal指向的对象,此处即是赋给了ival |
为引用赋值,实际上是把值赋给了与引用绑定的对象。获取引用的值,实际上是获取了与引用绑定的对象的值。同理,以引用作为初始值,实际上是以与引用绑定的对象作为初始值:
1 | // valid:refVal3绑定到了那个与refVal绑定的对象上,这里就是绑定到ival上 |
因为引用本事不是一个对象,所以不能定义引用的引用。
2.2引用的定义
允许在一条语句中定义多个引用,其中每个引用标识符都必须以符号&开头:
1 | int i = 1024, i2 = 2048; // i和i2都是int |
2.3左值引用
严格来说,当我们使用术语“引用(reference)”时,指的其实就是“左值引用(lvalue reference)”。
常规引用,一般表示对象的身份
2.4右值引用
敬请期待。。。
3.指针
3.1指针概念
指针(pointer)是“指向(point to)”另外一种类型的复合类型。
与引用类似,指针也实现了对其他对象的间接访问。然而指针与引用相比又有很多不同点。
- 指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。
- 指针无须在定义时赋值。和其他内置类型一样,在块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值。
3.2指针值
指针的值(即地址)应该属于下列4种状态之一:
- 指向一个对象。
- 指向紧邻对象所占空间的下一个位置。
- 空指针,意味着指针没有指向任何对象。
- 无效指针,也就是上述情况之外的其他值。
试图拷贝或以其他方式访问无效指针的值都将引发错误。编译器并不负责检查此类错误,这一点和试图使用未经初始化的变量是一样的。访问无效指针的后果无法估计,因此程序猿必须清楚任意给定的指针是否有效。
尽管第2、3种形式的指针是有效的,但其使用同样受到限制。显然这些指针没有指向任何具体对象,所以试图访问此类指针(假定的)对象的行为不被允许。如果这样做了,后果也无法预计。
3.3空指针
空指针(null pointer)不指向任何对象,在试图使用一个指针之前代码可以首先检查它是否为空。
1 | int *p1 = nullptr; // 等价于int *p1 = 0; |
在效验一个指针是否为一个有效指针时,我们应该倾向于
1 | if (ip != NULL) |
而不是
1 | if (ip) |
为什么有人会用if(ip)这种方式校验一个指针非空,而且在C++中不会出现错误呢?而且现在很多人都会这样写。
原因是这样的,
1 | /* Define NULL pointer value */ |
思考一道腾讯面试题:
1 | long a = (long)(((int *)0)+4); |
a = 8
1 | long b = (long)(((int *)8)+4); |
b = 24
3.4野指针
野指针不是空指针,是一个指向垃圾内存的指针。
形成原因
指针变量没有初始化。
任何指针变量被刚创建时不会被自动初始化为NULL指针,它的缺省值是随机的。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。例如
1
2char* p = NULL;
char* str = (char*)malloc(1024);
指针被free或delete之后,没有设置NULL,让人误以为是合法指针。
free和delete只是把指针所指向的内存给释放掉,但并没有把指针本身给清理掉。这时候的指针依然指向原来的位置,只不过这个位置的内存数据已经被毁尸灭迹,此时的这个指针指向的内存就是一个垃圾内存。但是此时的指针由于并不是一个NULL指针(在没有置为NULL的前提下),在做如下指针校验的时候:
1
if (p != NULL)
会逃过校验,此时的p不是一个NULL指针,也不指向一个合法的内存块,造成会面程序中指针访问的失败。
指针操作超越了变量的作用范围。
由于C/C++中指针有++操作,因而在执行该操作的时候,稍有不慎,就容易指针访问越界,访问了一个不该访问的内存,结果程序崩溃
另一种情况是指针指向一个临时变量的引用,当该变量被释放时,此时的指针就变成了一个野指针,如下1
2
3
4
5
6A *p; // A为一个自定义对象
{
A a;
p = &a; // 注意 a 的生命期 ,只在这个程序块中(花括号里面的两行),而不是整个test函数
}
p->Func(); // p是“野指针”