在关于指针的描述中,指针通常指的是用来保存内存地址的变量。在编程语言中,由于变量都是用来储存与它相同类型的数据,因此习惯上也使用指针来表示指针变量储存的数据(内存地址),也就是说指针这个术语可以表示一个变量,也可以表示这个变量里的内容。因此可以认为指针是在变量与数据的基础上的进一步抽象的数据类型,使用指针来描述具有相同性质的事物。所以说指针具有三种意义:1.数据类型;2.变量;3.数据。
数据和对象
数据来源于现实世界的识别与测量,如一个物体的长宽高的尺寸,或者一个人的名字、身高、性别的信息等,这些都是数据。
对象是数据在计算机内存中的表示,它是存储器空间里的某个片段。存储器空间的单位为1个字节,数据则表示为具有若干字节大小的对象。习惯上,也把数据叫作对象的值。
对象和引用
引用是对象在编程语言结构上的表示。有了用来表示数据的对象,随后的问题便是如何引用这个对象和它的值。引用对象是为了对对象进行读写操作;引用对象的值(或引用数据)是用来参与语句中表达式的计算。这在语言结构上表现为左值和右值的区别。有两种类型的引用:名字和指针。
名字引用:在创建对象时给它取个名字,即为这个对象指定一个标识符。这一过程在编程语言结构上叫作声明,即在储存器空间上专门划出若干个字节大小的区域用来作为存储数据的对象,并给这个对象指定一个标识符,在使用这个标识符的地方就是在引用它所对应的对象。
指针引用:
存储单元和对象。
地址是存储单元的引用,指针是对象的引用。
地址和指针都是数据,都要做为值存储在变量中,这种变量叫指针变量。同样是引用,但地址和指针使用的领域不同。严格来说应该区别地址和指针的用法。在由于在概念上指针兼容了地址的意义,而人们又普遍地接受和习惯了地址的使用,所以可作这二者是通用的。
数据类型
数据类型则是对不同长度的数据和变量的区分,相同类型的变量存储同样类型的数据。在计算机内所有的数据的表现形式都是二进制数字,不同数据类型的区别也就只能体现在数字长度上和对这个空间在使用上的细节规定。如在某些特定实现下,char和int是用长度不等的数据类型,int和float虽长度一样,但内部划分却不同。
指针类型和指针变量的关系类似于面向对象编程里的类与对象的关系,对象是类的实例,声明指针变量就相当创建一个类的实例。对象创建后,它的内容就是它所保存的数据。因此,指针是表示指针类型,还是表示变量的地址(变量保存的数据),或者表示一个指针变量,需要根据上下文来进行判断。
对象是数据在内存中的表示,存储不同种类的数据的对象以类型区分。对象由若干个连续地址单元构成,是经过分配后才能使用内存空间。对象是数据类型的实现,它的功能是引用数据。对象有以下特性:
对象的类型:指出该对象占用的存储单元的大小及划分,不同的类型的对象有不同的类型说明符。
对象的值:对象表示或引用的数据
对象的名字:一个用于表示对象的标识符,用来引用对象或对象表示的数据。标识符在表达式中做为左值时引用的是对象本身,作为右值时引用的是对象的值。有名字的对象才能叫作变量。
对象的指针:一个用于表示对象的二元信息结构,间接引用对象和对象表示的数据。其中一元是对象的地址,它是对象地址空间的首地址,表示这个指针引用的对象在内存中的起始位置;另一元是对象的类型,它是对象地址的关联的数据类型,表示这个指针引用的对象在内存中占用的地址空间的大小。地址在计算机中由一个数字表示,在32位系统中是4个字节的整数,64位系统中是8个字节的整数。对象的指针在表达式中做为左值时引用的是对象本身,作为右值时引用的是对象的值。
对象的可修改性:声明对象时使用修饰符const表示对象值是不可修改的,此时的对象叫作常量;没有修饰符const时表示对象的值是可修改的,此时的对象叫作变量。
对象的生存期:
对象的创建和销毁
内存经过分配后才能合法地使用,任何对未经分配内存的访问都会导致不可控的危险发生。创建对象就是地址空间中划出一片区域供程序使用,已经分配过的内存不能再次分配。收回这片地址空间相应地,当不再使用这块区域时就要销毁对象,以免造成内存泄露。
可以通过关联类型声明符和指针声明符共同声明一个指针变量,int * 才是完整的指针类型声明,指针声明符*说明p是一个指针,int则是p的关联类型,表示p只能保存那些类型与它关联类型相同的变量的地址:
int *p;
它的意思是:声明一个变量p,它是一个指向整型对象的指针变量。p关联到任何类型的变量,这些类型包括基本类型、指针类型、结构体类型、联合体类型以及枚举类型。
指针有两层含义,1、指针指向的地址,2、指针指向的类型,只有明白这两层意思才能弄明白指针是什么。用以下的描述方式就比较好理解,指针指向的是十六进制的值,而指针指向的类型则说明把这个十六进制值当作什么类型的变量来读写,int型还是double型。指针值说明了读写的起始地址,而指针关联的类型则说明了读写的长度。由于地址本身表示一个值,且系统默认把这个值当作一个字节来读写,因此可以把地址看作特殊的指针。
声明多个指针变量:
int *p1,*p2;
指针声明符*仅对与它相邻的标识符起作用,同一个声明语句中声明多个变量时,应于每个标识符前放置一个的指针声明符*。
声明指针时初始化:
int i=10;
int *p1=&i; //&i是变量i的地址,初始化为变量i的地址,指向对象i,成为变量i的指针,引用的值为10
int *p2=NULL; //NULL就是空,初始化为空地址,指向空对象,成为空指针,表示不指向任何对象,不是任何变量的指针,也就不能引用任何合法的值
指针赋值:
int i=10;
int *p1; //声明指针
p1=&i; //对指针赋值
指针可在声明时初始化,也可以在声明后赋值。未初始化的指针变量在声明之后会获得一个任意的地址,这个地址可能是程序数据区的地址也有可能是代码区的地址,这样的指针变量叫作野指针,对野指针进行访问会产生不可预知的结果导致程序崩溃。因此,任何指针在赋值后才能使用,最好的方法是在声明指针时初始化为空指针,使用时再赋值。
另外不要使用“指针的指针”、“指向指针的指针”这样模糊的表述,只会让你更加糊涂。这样说“指针变量的指针”、“指向指针变量的指针”。
变量i是一个整型对象,它的地址是&i,引用数据10。
变量p1是一个指向整型的指针对象,它的地址是&p,直接引用的数据是变量i的地址,间接引用数据10。
p是变量标识符,有以下几种叫法:
指向int型的指针变量
int指针型变量
int型指针
数据存在于内存中某个确定的区域,这片区域的索引信息就是指针,换句说指针是存储单元的引用。我们通过存储单元的引用来找到以及使用这个存储单元,引用(指针)包括两个方面的信息,那就是存储单元的起始位置和存储单元的大小。再通过指针来使用放在存储单元中的数据。总的逻辑关系是,数据保存在存储单元中,通过指针操作存储单元访问它保存的数据。计算机通过指针知道从哪个获得数据,以及这个数据的范围。存储这些索引信息(引用)的是指针变量,指针变量的内容就是指针数据,无论是指针变量还是指针数据都可简称指针。
要区别指针变量的存储空间和它所指向的存储空间,一个指针变量p指向某个存储单元i:p=&i。&是取址符,也叫构指符,&i的结果就是返回变量i的指针。把i的指针(&i)赋值给指针变量p,就说p指向变量i,就可以说p是i的指针。p和&i的类型是指向整型的指针类型int *,简单说就是整型指针型。含义是:p引用了名字为i的这片存储区域,这片区域的起始地址是&i,大小是sizeof(int);而指针变量p本身的索引信息是&p,类型是(int **),含义是p所命名的区域是以&p为起始,大小为sizeof(int *)。
指针只能存储与它同类型的数据,这些数据有以下几种来源:
int *i=&i; //&i是一个指针数据,通过地址符后跟着变量标识符获得(从普通变量来)
int *p=i; //p是在某处声明的一级指针变量(从一级指针来)
int *p=*d; //d是在某处声明的二级指针变量(从二级指针来)
无论指针变量还是指针数据都是同一种类型(类型相同,实现不同),都可以叫作指针。
有人说指针不是地址,地址不是指针,其实指针和地址是等效的,因为:
地址和指针都是对地址空间的引用,这个引用信息包含两层含义:地址空间的索引(在哪里)和地址空间的大小(有多大)。
它们的区别是地址描述的是最小地址空间,即地址所指向空间大小无论何时只有1个字节,而指针引用的地址空间大小依据其指向类型而定,即指针指向的空间是由多个地址(1个或1个以上字节)组成。
但它们的意义都一样,都说明了所引用空间的位序与大小,在C语言指针是对地址的继承和扩展,因此两者是通用的。
注意辨析这几个词的概念地址、指针、位置,他们都含有空间的意思。很多时候人们不自觉地把地址和位置当作一串数字符号来理解,这都对认识指针造成了障碍。
指针的指向类型:某某型的指针
用int型指针来代替向int型的指针,这种说法更简洁。某某型的指针,表明所对象是一个指针,它指向某某型的变量,意思是这个指针变量存放的某个指针值,在C语言中这个指针值就是某个类型变量的地址。如:
int a;
char b;
int *i=&a; i和&a都是指针,是int型指针
char *j=&b; j和&b都是char型指针
int **k=&i; k和&i都是int指针型指针
这样的表述方式更高效也不容易混淆。
从上可知,C语言中指针的获得方式有两种:指针声明符*和取址符&。
既然指针变量和地址都可以统一到指针的名称下,那干脆只保留指针这一说法,取消指针变量和地址这两个词如何?那当然是不行的。因为,指针只是对不同事物的共性的统称,具体还要根据使用环境不同来加以区别对待。那就是指针即可以用在存储空间上也就是存储单元或叫变量上,也可以用在对数据的称呼上如变量的地址数据。具体使用上的区别就是左值和右值的分别了。
当我们获得一个整型指针时,有以下用法
int *i,*p;
int j;
i=&j; i作为左值使用,i是整型指针,也是一个指针变量,当作存储单元用
p=i; i作为右值使用,i是整型指针,但却是指针值,当作地址数据来用或其他情况下作基类的数值数据用
因此在没弄清i的具体用法前就叫它为整型指针,当明确它的用途时就要使用指针变量和地址等术语加以区分。
相同的情况还有*i的用法。。。