正文之前
今天下午看了一下午的计算机组成与设计,结果好死不死的看到了设计部分--处理器的设计。天哪,我现在还只是一个准备给人装一台电脑做实验田的家伙,连用都不咋会,你还叫我设计!!!虽然我学过数电模电和电路原理,也学过单片机组成和应用,但是不代表我对这些逻辑元件感兴趣啊!!!所以很果断的,我到了晚饭时节就果断的抛弃了第四章跳到第五章--存储层次和并发设计,这才有了点重新活下去的勇气。然后晚间继续看神书--《C++ Primer 第五版》,这里面的只是真的是看得我如痴如醉,配合不少在计算机组成里面看到的原理知识,每天收获最大的就这会时候了!下面是今天的读书笔记,感觉很多都是各大公司招收IT人才的笔试题的选择题类!!
正文
1、 承接上一节的引用讲起
- 引用只是一个别名,是没有实际的地址和对象的。它只是绑定一个对象,好比是域名指向IP。然而指针不同。关键在于以下几点:
- 指针本身是一个对象,允许对其复制和拷贝,并且可以直接对指针对象本身进行操作,而不像引用没有对象实体,只能依靠别的对象的苟活。
- 指针不需要在定义的时候赋初值,因为它自己就是一个对象,是要申请存储空间的,完全不需要在一开始的时候确定指向的对象。
- 和其他的内置类型一样,如果在块作用域内定义的指针没有被初始化,那么就会是一个不确定的值。
2、 指针的定义的时候,书上有一种说法,如下:
int vdal;
int *pd=&vdal; // 正确:初始值是int型变量的地址
int *pd1=vdal; // 正确: 初始值是指向int对象的指针
对于第三行那个说法我是始终存疑的,因为在我的电脑上跑的时候是没法运行的,直接就报错了,所以大家定义指针的时候,还是直接按照第二行那个定义的方法吧,也许是第三行那种玩法太高端,反正现在我的编译器是跑不出来的~ ~
另外声明指针的时候指针类型和指向的对象的类型要一致,不然很容易出错,比如下面的代码:
double dval;
int *pd=&dval; // Error:int类型的指针不能指向一个double双精度浮点数的对象!!
不过有时候也是有特例的,后面容我慢慢介绍。
3、 指针的值
四种状态:
- 指向一个对象
- 指向紧邻对象的所占空间的 下一个位置
- 空指针,意味着指针没有指向任何对象
- 无效指针,也就是上述情况外的其他值
试图拷贝或者以其他方式访问无效指针的值都会引发错误。访问的后果无法预计。
4、利用指针访问对象
如果是指向一个变量,那么直接用*p就可以访问,如果指向一个结构体,那么形式有两种:
Struct *Item;
*Item.name="ZZB";
item->name="ZZB";
上面第二行第三行的含义是一样的。第一行是定义一个结构体,结构体内部有一个变量时name,所以如果要访问name的话。两种方式完全相同。
解引用(*)操作仅仅适用于指向了对象的有效指针。
下面几个很有意思,很容易混淆的定义或者是声明方法。我们一起来探讨一下:
int i=42; //这就是个定义
int &r=i; // 这就是个引用
int *p; // 定义一个指针,你看,不一定要初始化对吧
p=&i; // 给p这个指针对象复赋值 内容是i的地址
*p=i; // 这个跟上面是一个意思。就是说i赋给解引用后的p指针,等同于把i的地址给p;
int &r2=*p; // 这一句才是最骚气的,解读的顺序是:p指针解引用后被r2引用
上面透漏出来了一个计算机领域的常识:读一段代码,从右读到左边,按照优先级自己排列。
5、 空指针
空指针不指向任何对象,以下是生成空指针的几个办法:
int *p1=nullptr;
int *p2=0;
#incluede <cstdlib> int *p3=NULL;
以上三种空指针的定义方法完全等效,最直接的是第一种。在我们编程的过程中,如果实在不知道指针指向何处,那么上面三种任选其一,欢迎选购!
6、 void * 指针
void* 是一种特殊的指针,可以存放任何类型的地址,也就是说你复制的时候随便放一种都可以,相当于搬运车,来者不拒。但是我们也没法通过这个指针查询到任何它所指向的对象的内容,因为你不确定指向的对象是什么类型,那么也就没法准备相对应的类型的寄存器来存放取出来的对象值。
int p=23;
double q=1.2;
void *a=&p;
a=&q;
上面的所有代码都不会报错,因为void指针就是这么溜,可以接受任何形式的对象传递指针给它,但是也就是因为这么6,所以不管你的对象再简单,void也没法调用,从void指针的角度来说,地址就只是指向了一个内存空间,至于里面是啥,我不管,我只是记着这个地址。
所以如果要强制转换一个对象的类型的话,可以尝试用void * 指向。然后调用的时候强制转换指针类型、不知道行不行,待会测试下下面的代码就好了:
int a=12;
void *p=&a;
cout<<(double)*p<<endl;
7、 那些年定义指针踩的坑:
- 第一种,这两个含义不同吧??
int* p;
int *p;
其实呢,根本没啥不用,只是让你看着很警惕而已。实际上没有任何的不同,只是第一种写法容易让人误会。
- 第二种,我应该是都定义了两个指针吧?
int *p1,*p2;
int* q1,q2;
然而,现实往往残酷,第一行是定义了两个指针,但是很不幸的第二行是一个指针,一个整形数 ,至于谁是谁,我咋知道,自己猜?!!
8、 指向指针的引用
请客官查看下面代码:
int i = 42 ;
int *p ;
int *&r=p;
r=&i;
*r=0;
- 从第三行开始说起: 这里就可初现端倪,还记得前面说过的吗?代码从右边开始读,所以应该是定义了一个对指针的引用。此处r只是p这个指针对象的引用,也就是p的别名,int *是一体的。这时候可以用
cout<<*r<<endl;
得到42; - 至于第四行,就是定义了这个引用的值,也就是说此处等同于
p=&i;
- 第五行就不消多说了吧??等同于对i赋值
*p=0 or i=0;
9、 const限定符
- 整的来说,const的意思就是,我定义的这个东西,是雷打不动的,我这个对象,谁都不能动,除了对我进行修改,其他的你随意,但是如果你要通过别的方式妄图修改我,不好意思,你死定了(报错大法好!!)。不过如果里面是个地址,你通过我这个地址找到了另外一个对象,那么随你改,你删了都行,但是不许动这个地址,不然我跟你急!!没错,const的作用就是蛮不讲理的保护定义的这个对象不被修改!!任何威胁到const对象的行为都会被当作错误反馈
- 还有一点,const必须初始化,不然我保护一个对象,对象都没有被初始化,我保护个蛋哟~~所以const是必须被初始化的,不然就报错。见代码举例:
int i = 42;
const int j=i;
int k=j; //Right!!
j=100 //Error!!
很明显的,第三行是代表我可以取出const常量的值,但是第四行就好死不死的妄想去修改常量,这是大大的不对的!
10、 默认状态下,const仅仅在文件内有效
如果想要跑到别的文件里面去:
一种是重复定义,但是这个实际上相当于在不同的文件中定义了很多歌独立的常量,很容易引发错误。所以这正是我们要避免的,只能用另外的办法。
对于const变量,不管是声明还是定义,都加一个extern 就是上次学过的那个。这样就只需要定义一次,然后在别的文件中声明,但是记住,只能一个进行初始化,其他的都只是声明,不然冲突了就爽歪歪了。所以前面说过,为什么extern可以初始化呢?这不是跟定义一样了吗?这就是extern可以初始化的好处了!
11、 对const的引用
- 初始化和对const的引用
int i=42;
const int &r1=i;
允许把一个常量引用绑定在一个非常量上,但是因为引用本身不是一个对象,所以我们没法通过r1引用限制i是非常量这个事实!
const int &r2=42;
可以的,是一个常量引用!此处42是常量,对常量必须要定义引用也是“常量引用”
const int &r3=r1*2 ;
没问题,r1本身也是常量,定义常量引用绑定一个常量这是理所当然的!
int &r4=r1*2;
Error: 不行!!r4这个引用不是常量引用,意味着外界可以改变通过改变r4来改变它绑定的对象,但是你想绑定常量?做梦!报错!!!
- 结论
- 要引用常量,本身必须是常量引用或者是常量指针;
- 常量引用对象不一定要是常量,可以是非常量,见上面第二行
12、 指针和const
指针是对象,引用不是,所以允许定义指针本身是一个常量而不是常量引用。所以
!!常指针必须初始化,必须给出一个地址赋给常指针对象。不变的是指针本身的值,而不是指针所指向的那个值:
int numb=0;
int *const err=&numb;
const double pi=3.14;
const double *const pip=&pi
看第二行,从err这个对象的定义出发,从右到左看,首先遇到一个const 那么说明它是个常量,然后是* 说明它是个常量指针,然后遇到了int 也就是说它是一个指向int对象的常量指针,那么它初始化是啥?没错,numb的地址,完美符合!!此处定义了一个指向非常量的常指针,可以修改指针所指对象,但是不能修改指针,与之相反的,如果是指向一个常量,而指针本身不是常指针,那么可以修改指针本身这个对象而不能通过指针修改指向的这个对象!
看第四行,从pip出发从右往左看,const --常量,* -- 指针, double - 指向double类型的常量指针,const 说明指向一个常量。所以通篇读下来,不仅仅pip本身是一个double类型常量指针,所指向的对象也是一个double类型的常量!
那么我们的如下操作都是合法的:
*err=100;
cout<<numb<<endl; //output:100而不是0;
cout<<*pip<<endl; //output:3.14
cout<<pi<<endl; //output:3.14
所以啊,总结下里,const就是一个傲娇属性爆棚的家伙!自己是完全不能改变的,一旦有这个趋势,立马拉整个程序下水,报错!!如果自己指向别人,不管对方是不是常量,但是如果别人引用自己,那么就要求对方一定要是常量!!如果是用指针指向常量,那么就要求无法用指针修改,只能引用,至于你指针自己变不变,无所谓,反正你不能通过指针修改我!虽然这东西傲娇的不行!不过,我怎么这么喜欢这玩意呢???
正文之后
要睡了,今晚有点浪,明天继续看书,不过晚上想看看《羞羞的铁拳》 不知道是不是跟别人说的那样笑点十足,再改改睡觉!