C语言中为什么要设计指针这个东西,这个东西的设计原理是什么,本来就是个很简单的东西,小学生思维就够了。还有,这些原理之类的,对于使用C来编程的人来说,也是必须完全了解的,了解的深度与你编程时的轻松度密切相关,所以,弄清这个对你很重要。
总有人在问指针是干什么用的,那我先说说我的理解:获取某个数据所在单元的地址值,由此推算出其它数据所在单元的地址值,以用于找到这个数据所在的存储位置。 计算机编程,可以说,基本就是对存于存储器中的数据进行操作,这就牵扯到一个最重要的问题:你现在要操作的数据,存在哪个地方? 想要弄清这个问题,最根本的方法,是了解芯片的设计和工作基本原理与基本结构组成。当然,如果知道存储器的大概结构,也算是有了一个基础。这里我不讲这么多,因为学单片机的人,基本的知识应该还是有的,不是那种不与硬件打交道的程序员,所以不需我多讲。
C与汇编有两个根本的区别,造成了C中指针的产生:一个是数据所存储的地址是由C编辑器自动分配的,程序员没自主权,所以一个数据存储的地址,程序员是不知道的;二是C编程中所用到的数据的长度与存储单元容量的不一致导致一个数据可能会占据数个存储单元,由此造成数据存储地址值的不连续,这个现象造成的原因在于,不管你是8位机、16位机还是其它位机,一个存储单元的位数都是8位,这个是芯片设计就如此,原因自然主要是兼容问题而不得不做出的选择。本段所述的现象,在下面的比喻之中会进行解释。
用C语言编写的程序,在程序运行中,你所要处理的数据存在哪,你是必须知道的,否则你找不到这个数据在哪,你的程序就没法编下去了;如果你把数据所在的位置给弄错了,那这个程序在运行中就得不到你想要的结果。
有关数据存在哪,及如何找到这个位置,我打如下一个比方: 我们知道,宾馆的房间都是有编号的,而且是用数字来编写的,很统一,一个房间一个编号;每个房间的大小都是一样的,也就是说其容量是一样的。宾馆来了一家一家的,住哪间?宾馆前台来安排,一家人数少,可能就是一间,人数多了,可能就得几间了,也就是说,同样是一家,可能他只占据一个房间编号,也有可能占据数个房间编号(这就造成了地址编号的不连续),这个数据,只有前台知道。如果你想去找到哪一家,你怎么找?你一个房间一个房间地去看单子也好看现场也好,都不是个办法,一般情况下,每一家都是有个名字的,如张家、李家、王家、陈家等等的,你告诉前台,前台根据这个名字查一下,就会把这家的第一个房间的编号(首地址)给了你,而你在事先知道他们家人数及宾馆每间房能住的人数的情况下,也就知道了他们家占几间房。这个比方里,与C语言中的情况极其相似,就是存储内容物的位置都是用统一数字编号来标识的,存储内容的每一个单元(房间)的容量都是固定一致的,每一个(组)数据(一个家庭)都是有一个名字的,单个数据的长度(某个家庭人数)你事先是知道的,内容物的存储位置分配是由他人分配而你事先是不知道的,其占据几个存储单元数量你是事先知道的。
同样的原理,在C语言中,要找到一个数据的首地址,我们先要把这个数据的名字给编辑器(前台),编辑器就会依据这个名字把这个数据存储单元的首地址给你,这个动作,就是在这个数据的名字前面加一个“&”,其首地址值就出来了。 然后这里就又有了一个问题,这个查出的地址值,放在什么地方?宾馆是可以将地址值写在纸上,那计算机呢?它能放这个数据的地方,还是只有存储器,当然,你也可以为存储这些个地址而单独在芯片中设计一个专门的区域,那你可以算是创新,但是,现有的芯片怎么办?没办法,还是只能存于现有的存储器之中,于是,C编辑器还得给一个房间(存储单元)专门用于存储这个地址值,然后,这个单元编号是多少?程序员又不知道了,因为又是编辑器自动分配的,所以,没法,又得给这个取个名字以便查找利用,所以,在将要存入的内容存入之前,得先对要存入的内容进行定义(取名)一个变量,这样我们以后就可以用名字直接找到这个内容了。
下面就谈谈C中有关获取地址与使用地址的一些规则,有关这个概念的名词,现有教材都称之为指针,本人对这个名词比较不感冒,这个名词显然没能与“地址”一词紧密联系,于是不易理解不易记住(现有计算机教材中,类似槽点很多): 存放入单元的内容要要先取个名字,也就是所谓的定义(前面已经解释过),这个因为指针变量的功能不同,所以定义变量名时,也要给出区别,让编辑器知道这个变量的用途,也让读程序的人知道这个变量是用于存储地址的,也就是所谓的指针变量。其进行区别的规则是变量名前面加一个“*”以示区别,如果不加会怎么样?你照样可以将取得的地址值存入其中,但是,你用这个变量去取这个地址所指向数据时,程序在编译时就报错了,这个现象,只是编辑器的硬性规定,也是有道理的。其实编辑器在分配指针变量在存储器中的位置时,也没做什么特殊处理,它与其它变量都是统一分配地址的,基本也是顺序分配,谁先使用谁先排前面(不是谁先定义谁先排前面),一点也不特殊。
在使用指针来取得存储单元中的数据时,C的规定也是在指针变量名前加一个“*”以示这个是用地址值来取得数据,如a=*p,它的含意是:P所表示的存储单元中的数据,是一个地址值,a=*p就是把这个地址值所指向的单元中的数据送给a。其实吧,个人觉得这个“*”符号用得不好,改为“@”可能更让人容易理解,这个“@”有“在”的意思,然后我们就可以理解为“把在这个p值所指向的单元中的值,把它给a”。如果是这样,很多运算公式就很容易理解了,如@p++就是操作“在”p值为地址所指向的单元中的数据,这个具体操作就是将其加1,而p++就是p值加1个数据长度值以将p值变为下一个数据的地址值(不是地址值本身直接增加1,这个在后面再解释),也就是说,只要在p前面加了@,那要操作的对象就并不是p本身,而是它所指向的单元,这个一定要牢记不是操作p本身,这样你在编程时就不容易出错了。
指针变量是可以直接赋一个常量值的,但是,这个值必须是你所要用到的单元的地址值,如果不是,程序编译时不会报错,但程序运行时肯定是乱了,也就是说,前提是你知道这个数据的绝对地址值。
接下来的一个问题,就是指针变量的长度,既然是表示地址,那它就是一个没有负号的整数,其长度最长也就是存储器地址值的长度,如51机的RAM,其地址就只有八位,所以,在定义时,它的指针长度也就一个单元,多了浪费。 还有一个规则,指针变量的加减运算,是与其所指向的数据的长度绑定的,这个是在编译处理时用到的,意思是其尽管这个指针变量也是一个数据,但它在做加减运算时,例如p=p+1,并不是p值直接加1,而是加一个其所指向的数据的长度址,以直接变为下一个数据的首地址值,这个处理,并不是在程序运行中进行的,而是在编译时进行的,所以指针变量在使用之前一定要先定义,定义之后,编辑器才会知道这个是个指针变量,然后这样做处理。 还有一个教材中没说清楚的问题,这个所谓的指针,到底是只针对RAM,还是包括ROM?这个有兴趣的朋友可以研究一下,方法也不难。 以上所说,其实都可以用编辑器调试中的汇编窗口来证实。 以上纯属个人理解,如有错误与不当,万请指出。
|