C++ Primer读书笔记第二章③
C++ Primer
第二章 变量和基本类型
引用和指针
引用
引用就是给对象起了另外一个名字,通过将声明符写成&的形式来定义引用类型,可以把引用理解为取绰号,比如欧阳锋,他的引用就是西毒,西毒就是欧阳锋,是同一个人,对应到程序中就是同一个对象。
记住,引用不是对象,它不是人,就是个绰号,但对它的操作都是对其对象本身的操作
int ival = 1024;
int &refVal = ival; //refval引用了ival,ival的绰号是refVal,
//它俩在内存中表示的是同一块东西,一毛一样的
int &refVal2 //会报错:引用必须被初始化,你要给别人取绰号,没人怎么行?
可以通过对下面代码的理解来测试一下你对引用的了解程度
int ival = 1024;
int &refval = ival;
refVal = 2; //此时ival也等于2,对吧
int &refVal3 = refVal; //refVal3和refval以及ival都是2,是同一个对象
下面看代码注意引用定义写在一行的时候怎么区分
int a = 0, b = 1;
int &r1 = a, r2 = b; //r1是引用,r2是对象
int r3 = a, &r4 = b; //r3是对象,r4是引用
这样就知道了吧,引用符号&是跟着变量名r的,不是跟着类型说明符int的。
引用只能绑定在对象上,且类型要精确对应,不能与字面值或某个表达式的计算结果绑定在一起(有例外情况后面学习的时候我会提出来)
int &ref1 = 10; //错
double a = 3.14;
int &ref2 = a; //错,类型没有精确匹配
int b = 10;
int &ref3 = a+10; //错,不能与表达式的计算结果绑定
指针
与引用不同,它是一个独立的对象,只不过它的值只能是其他对象的地址。
int a, *b;//合法,a是int类型的对象,默认初始化为0,
//b是指向int类型对象的指针,因为它本身是对象,所以没有初始化也可以
那么我们如何获取一个对象的地址,并把该地址赋给指针呢?
int a = 1;
int *p = &a; //通过&符号取得对象a的地址,并把这个地址赋给p,注意是给p而不是*p
&符号在等号左边表示引用,在右边表示取地址符。
指针的类型要和它所指的对象严格匹配(例外情况我还是提出来,现在不用管)
double a = 1.0;
int *pi = a; //错了
现在我们已经能取到变量地址了,那么我们利用指针访问对象呢?还是看代码
int a = 10;
int *b = &a;
cout << b << endl;
cout << *b << endl;
这里输出了两个值(我在自己电脑上运行的) 0x9ffe44 10 很明显第二个值10是我们原来的对象,那前面那个是啥呢,是我们变量i在内存中的地址,0x表示16进制,现在知道怎么通过指针访问对象了把,千万不要忘记*号啊。 强迫症表示我还要再解释一遍
int *b = &a;
//大家注意这句话,a是一个内存中的对象,值为10,,这个很明确,那么b呢,b是啥,b
//其实也是内存中的一个对象,只不过它的值时a这个对象的地址0x9ffe44,所以我们直
//接cout << b的话就是输出地址,要通过b来访问a的话就要用*号,这里表示解引用符。
//可以把*b理解为b这个对象所指向的那个对象的值。
原书上还提到了空指针,有兴趣的同学自学吧,我的宗旨是尽量不用空指针,初始化所有指针,就像我们为了避免默认初始化而初始化所有变量一样。
指针和引用都能提供对其他对象的间接访问,但有很大的区别。最大的区别是引用本身并非一个对象,一旦定义了引用,就无法令其再绑定到其他对象,就是说引用作为绰号来说,很专一;指针本身作为一个对象,它存什么地址,就指向那个地址的变量。朝三暮四 下面看看代码的例子:
int i = 42;
int *pi = 0; //pi被初始化,但没有指向任何对象
int *pi2= &i; //pi2被初始化,存有i的地址
int *pi3; //pi3没有初始化,这是合法的,因为指针本身是一个对象,
//当然,我们要避免这么写,但它可以这么写
pi3 = pi2; //pi3现在和pi2指向同一个对象了,
//但它俩本身没啥关系,只是二者存的值相等,都等于变量i的地址
pi2 = 0; //pi2现在不指向任何对象
下面测试一下指针的掌握程度
int ival = 0;
int *pi = 0;
pi = &ival;
*pi = 0;
请问现在ival等于多少,pi指向谁?不公布答案了,不确定的话可以找个编译器调试下。
指针在if中,给你个代码自己体会
int ival = 0;
int *pi = 0;
int *pi2 = &ival;
if(pi) //false
{}
if(pi2)//true
{}
if(*pi2)//false
{}
if(*pi)//编译错误
{}
指针定义两种写法,随你高兴,效果一样
int *p1;
int* p2;
==既然指针本身是一个对象,我们就可以搞点事情了==
int ival = 12;
int *pi = &ival; //pi是指针,指向int型数ival
int **ppi = π //ppi是指针,它指向int*型指针pi,也就是说ppi是指针的指针
graph LR
pi-->ival
graph LR
ppi-->pi
其实你也不用觉得多复杂,记住本节开头的一句话:
指针是一个独立的对象,只不过它的值是其他对象的地址,这个对象是什么类型的,它就指向什么类型的,这个对象是指针,它就指向指针,这个对象是指针的指针,它就指向指针的指针,它就是指向指针的指针的指针~
指针的引用
因为指针是对象,所以可以对指针进行引用;反之则不行。 举例:
int i = 42;
int *p;
int *&r = p;
r = &i;
*r = 0;
这段代码将i改为0了,你看出来了吗? 开始p是个指针,r是p的引用(待会解释怎么看r是p的引用,你先这么认为吧),既然r是p的引用,那么给r赋值就是给p赋值,也就是说p和r现在都指向i,最后通过解引用r修改i的值为0。 怎么理解r呢? 以r为最右端,从右到左阅读r的定义,最近的是&,就是说r是一个引用,其余部分来说明引用的类型是啥,这里就是int ,说明r引用了一个int 类型的数。
这部分逻辑有点多,希望大家多看看坚持下,有问题可以讨论或者自己到编译器里试试