指针的自我修养

上传人:ji****72 文档编号:45674044 上传时间:2018-06-18 格式:PDF 页数:7 大小:426.52KB
返回 下载 相关 举报
指针的自我修养_第1页
第1页 / 共7页
指针的自我修养_第2页
第2页 / 共7页
指针的自我修养_第3页
第3页 / 共7页
指针的自我修养_第4页
第4页 / 共7页
指针的自我修养_第5页
第5页 / 共7页
点击查看更多>>
资源描述

《指针的自我修养》由会员分享,可在线阅读,更多相关《指针的自我修养(7页珍藏版)》请在金锄头文库上搜索。

1、指针的自我修养 指针的自我修养 妮露她爸 妮露她爸 指针的知识点较多,在各种类型的教科书中往往会将这些知识点划分为不同的小节进行讲解。比如,先概述什么是指针变量,继而阐述什么是指针常量,接着引入指针的指针 等等。虽然这个流程符合官方标准,但是往往给初学者造成的后遗症就是:学完了指针,却无法把指针的知识点串连起来,看书的时候觉得书上讲的都对,但是自己写程序时却检查不出到底错在哪里。 基于上述初学者对指针学习过程中碰到的不适症状,本文将着力打造一篇以独到的视角学习分析指针的随笔。专治“了解指针,但又不甚理解指针的特定人群。” 如果你从未接触学习过指针,那么请先看官方推荐的C语言教程,然后再来观赏本

2、文,以此达到曲线治疗的目的。好啦,废话不多说,让我们正式开始吧: 我们的讲解将从一个被广为流传的程序片段开始,程序片段如下所示: 这个程序片段在很多教课书中不只一次的出现过,要正真理解它,咱们还得从内存空间的分配上开始讲起。很多哥们看到这句话的时候就开始纳闷儿了:这说的什么跟什么呀,说着指针怎么就扯到内存空间上去了。好,那么下面我们就仔细来分析一下这句话的含义(这个分析的过程将是一个“源远流长”的故事):想必大家对这个赋值语句:int a = 24 绝对不会感到任何的难度。那么我们来看看在这个简单的赋值语句背后发生了一个怎样的过程呢? 这条赋值语句的功能很简单,将整型值常量24存储在整型变量a

3、中。很好,那么大家再细细地想一下:存储的前提条件是什么,当然是先要开辟空间喽(嘿嘿,自问自答了),比如你要在超市里存放包裹,超市的物品储藏柜肯定要为你开辟一个储物空间。那么问题来了,如果整型值常量24需要占据两个字节的空间,那么这个空间是在哪里开辟的呢(当然不是在储物柜上喽,对吧)。有的哥们笑了:这不有变量a嘛,整型常量值24不就存储在变量a中嘛。 听到如此有理的反驳,我竟无言以对。我不禁想起小时候看到过的一个故事:有个财主为了考验儿子是否知晓生活的疾苦(当然我一直怀疑是否存在过有情操如此亲民的财主),在吃饭的时候问儿子碗里的米饭是哪儿来的,儿子不假思索:这还用问,当然是佣人从咱家的粮仓里拿出

4、来的! 和碗里的米饭一样,程序中的存储空间也不是凭空产生的,而是从计算机的内存中拿的。当一个程序代码编译后就被载入了内存,程序运行过程中数据的存储都将在内存中进行。内存这个玩意人如其名,自然是用来存储东西的。大家完全可以把内存想象成是一个具有许许多多单元格的储物柜,每个单元格都标有独一无二的记号,在计算机术语中这个“记号”被称为“地址”。内存中的每一个地址对应着一个存储空间。有了存储空间一切都好办了。在商场存包时,我就可以把物品存储在编号为44的储物单元格里,有的顾客喜欢吉利的数字,也可以把物品存储在编号为66或者88的储物单元格里。看到这儿有哥们就说了,我懂妮露她爸的意思了。妮露她爸的意思是

5、想说:在计算机编程中,对于常量值的存储和商场中存放物品的道理是一样一样的。程序员在内存中找个固定的地址,然后将常量值24放到这个地址所对应的存储空间中就OK了。如果您这么想的话,那么有个问题不得不问问您:您在写代码的时候有碰到过需要确定内存中的地址编号的情况吗?有的哥们在看这篇文章的之前恐怕还不知道内存中还有地址编号这档子事儿吧。相信大家要存储一个常量(如:整型常量值)无非就是使用如下的方式: int 整型变量名 = 整型常量值; 咱把它写的具体点儿就是:int a = 24 ; 有的哥们沉不住气了,刚才还在讲内存中的地址,怎么突然扯到变量了。其实,内存中的地址和变量名之间的关系几句话就可以说

6、清楚啦:内存中的每个位置由一个独一无二的地址标识,计算机硬件永远是通过这些地址标识来访问内存中的各个地址(计算机可不鸟你什么变量不变量的,在它们的眼中就只有地址,你要在内存中存储数值是吧?好,等等,我给你找到个编号为XXX的地址,你就把数值放在该地址对应的存储单元中吧;啥?你现在要使用刚才存储的那个数值?行,稍等,我去找编号为XXX的地址然后把里面存的数据给你取出来),换句话说,如果你真的牛逼到能够记住计算机中内存的所有用户合法存储空间,你就可以抛开变量,直接将数据存储到任何一个合法空间中(当然,不同型号的计算机内存用户空间也不相同,要完成这个任务几乎是不可能的)。正是因为要记住所有的这些地址

7、实在是太痛苦了,所以高级语言提供了一种特性通过名字而不是地址来访问内存的位置所以高级语言提供了一种特性通过名字而不是地址来访问内存的位置。这些所谓的名字,就是大家最熟悉不过的变量。 这些变量与内存中具体地址的对应关系跟程序员没有半毛钱关系,换句话说,咱们程序员可不管自己创建的变量会与内存中哪个具体的地址进行绑定。这种情况其实很贴近生活,大家想呀,你开车去酒店,到了酒店门口,自然有车童来帮你泊车,你只要将车钥匙交给他就行了,你难道还关心他把车泊到哪个车位吗?同样的道理,你从酒店离开时,只要找到刚才那个车童让他把车给你开过来就成了。在整个存车取车的过程中,你都不需要知道你的车曾经被停在哪个车位,因

8、为你也根本不关心这个问题。这个比喻中,你就是程序员,车童就是变量,停车位置就是内存中的具体地址。 那么有些偏要刨根问底的哥们就要忍不住了:程序员既然不关心也根本不接触内存地址,计算机硬件也根本不通过变量来存取数据,那么变量和内存地址的关联是由谁来建立维系的呢。嘿嘿,这个幕后维系者不是别人,正式编译器。编译器在对你的源代码进行编译的时候会将程序员创建的变量与具体的内存地址进行关联,在这里你完全不用担心编译器会不会将某个变量关联到非法的内存地址上。当编译完成,程序运行时:a = 24 就意味着将24存储到a关联的内存地址空间,同理如果你要使用24(比如将其加1)只需这样做就可以了:a+(看到了吧,

9、内存地址对于程序员来说是完全透明的)。 上面讲了一大坨的内容,大家想必看的也累了,通过这些分析大家应该对赋值语句:int a = 24 有了更深一层的理解了吧。通过透过现象看本质,大家的理解现在可以抛开表象的变量了,这个赋值语句背后进行的行为可以用这样一句话来描述:在内存中开辟一个两个字节的空间,然后将整型值常量24放进去在内存中开辟一个两个字节的空间,然后将整型值常量24放进去。所以,这里的变量名a大家完全可以将它当成是一个存储空间存储空间来看待。 int a = 24 int a = 24 看到这里,回顾下大家之前所学的内容,有人或许会感到困惑了:如果在赋值语句int a = 24中,变量

10、a被视为存储空间的话,那么接着如果紧跟代码:int recv = a。此时的a貌似就不能被再视为存储空间了,而应该被视为该存储空间中存储的数值24,如下图所示: int recv = a int recv = a 这个问题问的很好,它涉及到编程中“左值”和“右值”的概念。同一个变量,作为左值使用和作为右值使用,所代表的含义是不一样的。当a作为右值右值使用代表该存储空间中所存储的数值代表该存储空间中所存储的数值;当作为左值左值使用代表存储空间本身代表存储空间本身。在下面结合指针的讲解中我们还会用到左值和右值的概念。 回到问题本身,我们来看语句:int * b = &a 。这条语句看似简单,我估计

11、很多哥们都能将这条赋值语句的作用倒背如流了:将变量a的地址赋给整型指针b,使得b指向变量a。 那么事实真的是如此吗,或者说你真的认为自己理解了这句看似简单的赋值语句了吗,让我们接下来细细地进行分析: 在这条赋值语句执行后很多哥们都会产生这样的一种错觉:变量a的地址已经存储在指针变量b中,所以此时指针变量指向了整型变量a,上面的这个图例应该完善如下: 这种指向关系目前存在吗,很抱歉,不存在!至少到这一步的时候还没有存在。很多哥们之所以会认为在执行上述的赋值语句int *b = &a 后会出现这样的指向关系,究其原因是因为这些哥们深信:指针变量存在着一种魔力,它与普通的变量不同,内含这种指向的特性

12、。那么事实究竟如何呢?事实就是:指针变量和普通的变量之间没有任何的本质区别,指针变量不含有自动指向的任何特性指针变量不含有自动指向的任何特性。话句话说,指针变量b所存储的数值100就是一个普通的数值,和24一样,也就是一些普通的由0和1组成的比特位。这些比特位不会因为存储在指针变量里而获得任何的特殊魔力(如指向另外一个变量)。 那么什么时候指针变量能指向另外一个变量呢?答案是:采用间接引用(*)之后,也就是使用表达式*b之后这种指向关系才建立起来。 请大家牢记:指针变量从其本质上讲与其它的整型变量,字符型变量,浮点型变量没有区别。程序员定义一个指针变量后,计算机同样要为它开辟一个内存空间,同样

13、要分配给它一个内存地址;指针变量和普通变量所存储的数值都是比特位的集合, 任何一个数不会因为存储在指针变量中而获得任何的特殊待遇。 但是,指针变量不直接存放程序员要存放的数值本身,这也就直接得出了一个非常容易理解的结论: 声明一个指向任何类型的指针(假设指向整型)都绝不会创建用于存储整型值的内存空间。 声明一个指向任何类型的指针(假设指向整型)都绝不会创建用于存储整型值的内存空间。 如果你还是不清楚这句话的含义,没有关系,相信通过下面这个例子的讲解,你肯定能完全领会我的意思了,我们还是拿顾客去超市购物时存放包裹的场景作为例子吧: 很显然上图中的储物柜就是内存,X号储物箱就是内存中某个存储单元的

14、地址。在还未引入指针变量的概念之前,客户就是这样存放他们的包裹物品的。现在我们引入指针,如下所示: 很显然, 右边的就是指针变量了。 编译器同样要为指针变量开辟内存空间, 而且编号为 “B号储物箱” 。但是这个B号储物箱中存放着顾客的包裹物品吗?显然没有。它存放的是一张纸质的号码牌,号码牌上写的是“2号储物柜”。现在大家明白了吧,这个号码牌有任何的魔力(如果这个号码牌能自动指向2号储物柜那就真见鬼了)吗?显然没有,这个号码牌和2号储物箱中存放的包裹一样都是普通的物体;B号储物箱中存放顾客的具体物品吗?显然没有,顾客的包裹被存放在2号储物箱中。B号储物箱不直接存放顾客的实际包裹物品B号储物箱不直

15、接存放顾客的实际包裹物品。我们把这句话从指针变量的角度来阐述那就是:指针变量的出现不会创建任何的存储用户实际数据的空间(如客户的包裹),所以你在使用诸如像int *b = &a这样的赋值语句之前必须要确认真正的储物空间(变量a)已经在这条赋值语句之前被创建必须要确认真正的储物空间(变量a)已经在这条赋值语句之前被创建。这个道理很显然易懂,如果号码牌中所记录的2号储物柜在现实中根本不存在,那么客户的包裹根本就无法存放,B号储物箱中存放的这张号码牌也就毫无意义。 好了,现在我们来讲讲指针中非常重要的一个操作符“*”,也即“间接访问操作符”。我们先给出间接访问的定义吧:通过一个指针访问它所指向的地址

16、的过程称为间接访问。用于执行间接访问的操作符是单目操作符*。这句话在很多的教材中都能找到,但是不幸的是,很多教材都没有对间接访问操作的结果在左值和右值的角度上进行区分,下面我们来仔细分析下左值和右值的不同结果: 假设,现在有这样的两行代码: 先来看变量ch,如果这个变量被作为左值使用时(也就是上面代码中的使用方式)它被视为某个内存地址所对应的存储空间存储空间,而不是该存储空间中所存储的数值(字母a);如果这个变量被作为右值使用时,那么结果就是该存储空间所存储的数值,也即字母常量a。如下图所示: 同样的,指针变量作为右值和左值的时候也会产生不同的效果,如下所示: 表达式:*cp,作为右值右值的过程:当对指针变量cp采用间接访问操作符*的时候,计算机会访问变量ch的内存地址(该内存地址即:&ch),找到该内存地址后查看该地址所对应的存储空间中所存储的内容所存储的内容,也即字符常量a; 表达式:*cp,作为左值左值的过程:当对指针变量cp采用间接访问操作符*的时候,间接访问操作符会找到其后面的操作数(这里的操作数就是变量ch的内存地址即:&ch)所代表

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 行业资料 > 其它行业文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号