基础概念复习
由于C++比较底层,能直接操作到内存,同时C++又可以进行面向对象的程序设计。因此C++中的“指针(Pointer)”和“引用(Reference)”这两个概念都比较重要。先对几个基本概念进行复习。
左值和右值
程序中最常见的可以说就是用=
进行的赋值操作。而针对赋值操作,就有了左值和右值的概念:
- 左值(lvalue)表示的是一个内存位置,往往是一个变量名称。既可以出现在
=
左边,也可以出现在=
右边; - 右值(rvalue)则往往是具体存储在内存中的某个具体数值。可以出现在
=
右边,但不能出现在=
左边。
如int a = 100
,表示将100
这个右值,存储在了变量a
所表示的占据4个字节的int
类型的内存位置;而int b = a
中,变量a
为右值,变量b
为左值。因此是取变量a
所对应的具体数值,存储在变量b
表示的int
类型的内存位置。总而言之,C++中一个变量的名字,实际上对应了内存位置和具体数值两个内容。如下图:
符号重温
一直以来就对C++中*
和&
这种比较“非人类”的标记比较头疼,在这里再把这些符号再复习一遍,同时巩固一下对应的概念。
首先一定要区分这两个符号用在左值和右值上的区别:
*
用在左值时,如用在定义变量的时候,表示定义了一个指针变量。如int *pt
,定义了一个int
类型的指针pt
,该变量的具体数值为一个内存地址,以该地址为起点的4个字节表示一个整数;*
用在右值时,如用在指针变量前,表示解引用操作,即取变量数值所对应的地址上的具体数值。如int a = *pt
,表示以指针变量pt
数值对应的内存地址为起点,读取4个字节,解析出整数,将该整数的值赋值给变量a
;&
用在右值时,则表示取地址操作。如int a
,则int *pt = &a
则表示取变量a
对应的int
类型的地址,赋值给指针变量pt
上述三种用法可以用下图总结:
但C++中还有一个特性就是引用。将&
用在左值上,如在定义一个变量时加上&
,则表示定义的是一个引用变量。引用变量必须要在定义的时候就初始化,使得该变量一开始就指向某个具体变量。实际上定义一个引用变量,就相当于将一个原有变量所对应的地址和数值新增了一个名称:
如上图,定义指向a
的引用ref
,实际上就相当于在变量a
对应的地址和数值上增加了一个名称。因此之后无论是修改a
还是修改ref
,对应地址上的数值均会发生改变:
符号位置
在使用*
定义指针,或&
定义引用时,符号靠近类型还是靠近变量名都是可以的,如下面两句都是合法的:
int a = 100;
int* ptr1 = &a;
int *ptr2 = &a;
但一定注意在一行定义多个变量时,每个变量名前是否加上符号,才代表该变量是否是指针或引用。如int *p, i
,很明显p为整数指针,i为整数,即相当于int *p; int i;
;而若写作int* p, i
,仍然是p为整数指针,i为整数!若想要同时定义多个指针,应该使用int *a, *b, *c
。
比较地址和引用
最后,比较一下地址和引用的一些差别。首先按下图定义各种变量:
即分别定义并初始化了整数变量a
和b
,二者明显对应的是不同地址的不同数值。之后int &ref = a
定义了一个指向a
的引用,即将a
对应的内存和数值新增了一个名称ref
。最后int *pt = &a
,定义了一个指向a
的指针pt
。与引用ref
不同,指针pt
实际上占用了一块内存,只是这块内存存储的值是变量a
对应的内存地址。因此对该指针解引用*pt
实际上也就对应变量a
,作为左值时表示对变量a
重新赋值,作为右值时表示取变量a
对应的值。
之后重新赋值引用ref = b
:
由于引用ref
、变量a
和指针的解引用*pt
实际上指向同一地址,因此这些变量的值都会因此而改变。不过此时变量a
只是在数值上与变量b
相同,二者仍然占据了不同的内存。
之后重新赋值变量b
:
如上所述,变量a
与变量b
二者仍然占据了不同的内存,因此重新赋值变量b
也并不会影响到变量a
,以及指向变量a
的引用ref
和指针pt
。
最后重新赋值指针pt
:
可见将之前指向a
的指针修改为指向b
,实际上也就是修改了指针变量pt
对应的具体数值为变量b
对应的地址。
总而言之,引用与指针的主要区别总结为如下几点:
- 指针可以为空,但不存在空引用,引用必须连接到一块合法的内存。
- 由上面一点容易推出,引用必须在定义的时候就被初始化。而指针可以在任何时候被初始化。
- 引用被初始化后,也不能再重新指向另一个对象。而指针可以在任何时候被重新赋值,而指向到另一个对象。
- 引用变量并不占据内存,仅仅相当于让同一块内存有了两个名字,修改任一个名字的值,都会影响到另一个,但两个名字指向的内存始终是同一个;而指针变量本身也要占据一块内存,占据内存的大小由操作系统的位数决定,32位系统占据4字节,64位系统占据8字节。因此指针变量也存储一个具体的数值,这个具体的数值就对应着该指针目前连接的变量的地址,可以随时改动。