1. 简述
用C语言写的代码基本上都用到指针,掌握好指针的概念对学好C有很大帮助。 为了方便理解我们可以把指针称作某一块内存的名字,通常计算机的内存会被分成许多小块,而每块都可以有一个名字,而实际上它每一块内存(一般一字节为一块)是有个编号的。为了更为直观,我们可以把全部内存比作一个大柜子, 这个柜子里面都是抽屉,每一个抽屉都有一个真实名字,而指针则是我们为某个名字起的绰号(用以区分真实地址)。
2. 实操
(1) 我们通过char * p声明了一个char类型的指针, 对照我们前面的假设, p 是柜子里抽屉的名字,就好比1号抽屉,2号抽屉里的1和2, 只不过我们称它为p,而 * 是一个动作,等同于拉开抽屉取出类型为char的东西, 至于类型是什么呢?就好比我们在抽屉里放的苹果/雪梨,它属于水果类型,如果我们放刀/枪/剑/戟,类型可以称为武器, 我们可以通过 int * p, 声明一个异于char的指针。
(2) 由char *p这个声明出发,我们首先要明确几点:
<1> p 必须为内存的地址名的别名,我们假定了柜子里有10个抽屉,由上而下为1号到10号, 那么这个p必须为1到10号这10个代号之一的别名. <2> 我们定了一个内存位置,这个位置的数据必须是char类型,就好比指定了一个只可以装水果的抽屉。 <3> 整个char *p指的是 在某个内存位置的数据,假如这个数据是 "老婆”,那么 char *p = "老婆“,就如1号抽屉装的是苹果,我们可以这样说:水果*1号=“苹果”。 <4> char *p这样的格式是用来声明的, 之后可以用 p 来代表整个char *p,这可能和前面所说的产生歧义,前面说p是个地址别名,现在怎又变成了char *p呢?其实这没错,就如我们人的名字,名字本来就是名字嘛,但别人叫你的名字,名字就变成你了。
(3) 通过上面的比喻,我们可以从 char *p 进阶到 char **p, 很轻易的得出char * *p= char * (*p), 根据2(2)我们可以知道,char *(*p) 里的(*p)必须为内存里的地址名,而*(*p)也必须是地址别名 ,所以整个的理解就好比一抽屉里装着另一个抽屉, 我们期待的东西则是抽屉里的抽屉的东西,现在中当不会这样,但一个抽屉里面可以放着一张 纸,纸里写着另一个抽屉的名字,好比我们玩寻宝游戏,意思就是说你要取的东西不在这个抽屉中,请根据抽屉里提供的线索寻找。 依此类推我们可以理解更多(*)的 char * * *p.
(4) 可以对名字进行修饰的除了 *外还有一个 &, 假定我们为1号抽屉起了个名字叫”抽筋“,那么柜子里就多了一个叫”抽筋“的东西,这个东西必须有个地方存放,也就是说这个”抽筋“是存放在某个抽屉里的东西, 但究竟存放在那个抽屉呢? 很简单我们只需&"抽筋”就能知道它是放在第几号抽屉里了。 内存里每一个字节都有一个编号,指针是我们赋给编号的别名,好比人的绰号, 我们帮内存起了个绰号,必须让内存知道,让内存知道的办法就是把这个绰号保存在内存里了, 比如 char *p, p是内存的绰号, 而p存放的位置&p可能会有另一个绰号叫“我是屁”。 就假设把一块内存为成10份,第一份叫1, 这是内存自己真实的名字, 但我们帮它起了个绰号叫"一哥“, 这个”一哥“里面放有钱, 假定钱为char类型是,于是我们可以这样写 “char * 一哥 = 钱”, 但因为”一哥“是我们的叫法, 内存不知道, 于是我们必须把一哥也存放在内存里,假定放到了第二份内存2里, 则“&一哥 = 2” 。
3. 进阶
通过以上的学习我们基本上明白了char *p, 和 &p 是什么意思了, 下面我们来进一步区分 const char *p, char * const p, 注意:const char 和 char const 其实是一样的。
(1) const char *p 表示 char *p is const, 根据上面的抽屉原理(注意此原理非彼原理), p是一个抽屉名, char *p则为抽屉里的东西, 而const则限定了抽屉里只能为不变的东西,何为不变呢? 假定抽屉装了五个苹果,如果用const去限定了,则不能增加和减少这数量,更不能把苹果换成雪梨,亦即说char *p里面的东西不能变更,只能读取。虽然char *p里的东西不能变,但我们却是可以改变p的指向的, 假如p原先是指向1号抽屉的,我们可以把它改为指向2号抽屉, 就比如: <1>const char *p = "GG"; <2>char *e = "MM"; <3>p = e 这样p就由GG变成了MM。 这就是const char *p了。
(2)通过3(1)我们很容易理解 char * const p, 意思就是 p 的指向不能变更了, 而且这个p必须一开始就要指定指向哪里,如指定了1号抽屉,那以后就是1号抽屉了,而至于抽屉里装什么东西,这却是可以变更的。
(3) 依此类推,我们可以掌握 const char * const p等含义。
4. 不得不说的指针与数组
(1)什么是数组呢? 数组就是内存里的一段固定的连续的区域,固定说的是置已定,用抽屉去解释就是编号连在一起,从上而下(相对来说,可从左到右、从下到上)相叠在一块的N个抽屉,且里面装都是同一类型物品,我们称它们为组。
(2)我们知道指针是某一块内存的别名,而数组又是内存的N个块的组合,这样两者的关系非常明显了。我们可以把指针指到数组里,如果指针刚好指在了数组的开头元素的内存块上,我们就可以把这个指针和这个数组对等起来了,而实际上我们也是这样定义,一个指针可以等于数组的名字, 如char a[5]; char *p; 然后 p = a 是正确的, 但反过来却不可以的, 因为数组的位置已经固定了,不能再变的, 所以我们不要奇怪p可以p++, a却不能a++。
5. 空指针和内存泄漏
(1) 空指针是指:"指针的指向还没有确定"。就是说你心目中有一个很的绰号,但这个绰号你还没有想好送给谁。如:char *p ; 编译后内存只会分配一个存放p的空间,而这个p究竟指向那里,就不确定,这需要你做下一步的指向工作,如:char *e = "我思故我在”; char *p ; p = e ; 这样p就有了明确的指向。在未确定p的指向前,我们千万不要往p里写东西,如:p="XX“,这是错误的。
(2) 内存泄漏是指:"某个区域的内存被遗忘了"。这个遗忘有如“你本来有100元,但其中的50元却不知道放去哪里”般悲哀。当然这50元你可能在以后的某个时间里找了出来,但在当前你想要用时,却没得用,这是种痛苦。内存的泄漏也一样,一段已给分配给你的内存白白的丢失了,它只能在程序退出后被系统回收去。导致内存泄漏的原因的前提是自已要管理内存,如通过malloc申请得到了一段内存空间,为了记忆,你必须帮这空间起个名字,比如叫 swap, 这个swap跟你一段时间后,你觉得不够用了,于是又申请了一段空间,因为swap跟你久,你意识中把新分到的空间又叫swap,于是前一次申请的空间就丢失了,这个丢失的意思是你不知道怎样称呼它了,所以你就无办法找到它了,就像掉了某人的电话号码般,你无法再通过电话找到他,又像是某人的电话号码被别人用了,你通个号码找到的再不是你心目中的某人。
|