C++Primer阅读记录-复合类型-指针-引用-多级指针-

复合类型指的是 *基于其它类型定义的类型*, C++中主要的两个复合类型有:引用和指针

引用

引用可以理解为别名,也就是为变量起了另一个名字。通过将声明符写成&d的形式来定义引用类型,其中d是声明的变量名。

~~~C++

int ival = 1024;

int &refV1 = ival; //合法,为ival起了一个别名refV1

int &refV2; //不合法,引用必须被初始化

~~~

引用在初始化时,会被与初始值绑定在一起,操作引用就是在操纵与引用绑定的对象,也就是说,如果为引用赋值,同时也会改变绑定的值(因为二者本就是一个东西,好像一个同学叫做AAA,我为他取了个外号叫3A,那么3A和AAA指的就是同一个人,并且3A不能再是别人的外号)

需要注意的是,引用本身并不是一个对象,他只是为另外一个对象起了一个新的名字,相当于是一个标签,并且这个标签一旦贴上去,就撕不下来了。所以我们无法定义一个引用的引用,因为引用是要指向一个对象的。

指针

指针与引用类似,也实现了对另一个对象的间接访问。与引用的不同在于

  1. 指针本身也是一个对象,允许对指针赋值、拷贝,并且指针可以在生命周期内先后指向不同的对象。而引用本身不是一个对象,无法定义引用的引用,也不能指向初始值外的对象。
  2. 指针可以不在定义时赋值。而对引用而言,这样做是不合法的

也正是因为引用不是一个对象,所以指针是无法指向引用的。

定义指针要写成*d的形式,d是变量名。

需要注意的是,如果在一条语句中定义多个指针,每个变量名前都要加*.

~~~C++

int* p1, p2; //实际是定义了一个int指针p1,和一个int变量p2。符合*只对p1起作用

int  *p1, *p2; //两个指针

注意:

符号* 紧跟类型说明符后面 或 变量名前面,这两者是等价的。

推荐使用int *p这种形式,因为

int* p1容易误导人,例如int* p1,p2; 让人以为*是对后面所有声明符起作用的,但实则只对p1有效。

~~~

获取对象的地址

指针存储的是该对象的地址,要想获得一个对象的地址,需要取地址符&

~~~C++

int val = 1;

int *p = &val; //p存放的是val的地址

~~~

需要注意的是,除了两种例外情况(书56页和534页),指针必须指向对应类型的对象,否则报错。例如int指针不能指向一个double对象。

指针值

指针的值有以下4种状态:

  1. 指向一个对象
  2. 指向紧邻对象所占空间的下一个位置
  3. 空指针,没有指向任何对象
  4. 无效指针,即除以上三种情况外的指针

试图拷贝、访问无效指针都将报错。

通过指针访问对象

通过解引用符*,可以访问对象。

~~~C++

int val  =1;

int *p = &val;

cout<< *p; //输出1

~~~

解引用的意思,其实就是取出指针指向地址所存的值。例如本例,p指向val的地址,而val的地址上存的值是1,所以*p为1.

修改*p,会直接修改地址上存的值,所以val的值也会改变(因为二者都在取同一地址的值,修改也是在一个地址上修改)

空指针

空指针不指向任何对象,有三种初始化空指针的方法

~~~C++

int *p = nullptr;

int *p = NULL;

int *p = 0;

~~~

nullptr是C++11新标准的一个方法,它可以被转换成任何一种其它类型的指针。NULL是一个预处理变量,在cstdlib中定义,其值就是0.但是在现在的程序中,在定义空指针时,最好使用nullptr。

注意:

指针是C中的一大难点,容易造成各类问题。例如,访问未经初始化的变量可能造成意想不到的后果。如果使用了未经初始化的指针,那么指针所占内存空间的当前内容会被看作一个地址值,如果这个地址恰好有内容,我们就很难分辨这个是合法还是非法的了。

因此,建议初始化所有指针

指针的操作

~~~C++

int i = 123;

int *p1 = nullptr; //空指针

int *p2;   //未初始化的指针,不要这样做

p1 = &i; //指针p3指向i

p2 = p1; //p2和p3指向了同一位置。

/*

为什么这里不用&p3?

原因在于,之前说过,指针本身存储的是一个地址值,所以p1本身就已经是一个地址值了,所以可以直接通过p2 = p1的方式,将这个地址赋给p2,使p2也指向这个地址。而i是一个int变量,i的值是123,而不是它的地址,所以需要通过取地址符&i,来使得p1指向i对应的地址

/*

~~~

指针只要有合法值,就也可以用于表达式中。

如果指针的值是0,返回false,任何非0指针返回true。

~~~C++

int i  = 1024;

int *p = nullptr;

int *p2 = &i;

if(p){ // false

}

if(p2){ // true

}

if( p == p2){ // false

//如果两个指针指向同一地址,则返回true,此处是false

}

~~~

void*

void*可以存储任意对象的地址,但是我们对其中存储的是什么类型的对象并不了解。

通过void*,我们可以拿他与其它指针比较,也可以作为函数的输入输出,或者赋值给另一个void*.

指向指针的指针

指针可以指向指针,*的个数没有限制,我们按照逻辑进行解释即可。通过*的个数,我们可以划分其级别。也就是说,**表示指向一个指针的指针,***表示指向指针的指针的指针。

~~~C++

int v = 1;

int *p = &v;

int **p2 = &p;  //注意,这里用的是&p。 因为p的值,是p地址位置所存储的值,这个值是v的地址,而我们现在需要的是p的地址,所以需要取 &p

~~~

通过一个表,来为大家解释一下各个值。

解释

v

1

int v的值是1

&v

0x0001

v的存储地址(我这里只是代指,真实的地址位数取决于系统)

64位系统本身应该有16位十六进制数(16*6=64位),但是大家打印出来可以发现只显示12位,原因在于pc几乎达不到128T内存,所以有4位十六进制数全0或全1,不显示

p

0x0001

p指向了v,也就是说p存储的值是v的地址

*p

1

p存储的地址是v,v存储的值是1,所以解地址取得1

&p

0x0002

p本身也有一个地址是0x0002,这个地址上存储的值是0x0001

**p2

1

可以理解为 *(*p2),而*p2的值是&v

*p2

0x0001

*p2存储的值是&v,指向的是p

p2

0x0002

p2存储的值,是p的地址

&p2

0x0003

p2本身的地址

只要时刻记住,*xxx是要把&xxx上存储的值A(一个地址值),这个A地址所存储的给取出来,就不会搞混。

指向指针的引用

int i =1;

int *p;

int *&r = p;

r = &i; //将 p指向i

*r = 0; //修改i为0

可以看到,其实现在r就是p,*r就是*p,r是p的一个别名。

分析复杂度声明语句时,应该从右往左读。例如int *&r,  &对r的影响最直接,说明r是一个引用,在往左看,*代表r引用的是一个指针。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值