对于指针的理解一直比较混乱,此处分析一些简单类型的指针加深自己的理解,
指针的理解
指针的定义:
int p; //定义普通整型变量;
int p; //定义一个指针p,指针类型是 int 型,而指针指向的内容的类型为 int 型;int p[3]、int *p[3]和int (*p)[3]
int p[3]定义了一个包含三个int型数据的数组。
int p[3]与int (*p)[3]应该一起理解,其中int *p[3]定义了一个指针数组,而int (*p)[3]定义的是一个数组指针:从优先级上考虑int *p[3]中,p首先跟[ ]结合,所以,p首先是一个数组,然后再跟结合,说明数组里面的元素是指针类型的,最后是与int结合,说明数组中的指针类型是int 类型,而该指针指向的内容是int 型。指针数组,即存放指针的数组。 还是从优先级考虑int (*p)[3]中*P通过()强制改变优先级,此处p首先跟结合,所以,p首先是一个指针,然后p再与[ ]结合,说明指针p所指向的数据类型是数组,最后再与int结合,所以数组中存放的数据类型是int型数据。因此,int (p)[3]是一个数组指针,即指向数组的指针,其中指针类型是int ()[3] ,即指针指向一个带有3个元素的数组,指针指向的数据类型是int ()[3].int p( int )和int (*p)( int )
int p( int )表示的是一个函数,p前面的int表示函数返回类型,括号中的int表示函数参数。int (p)(int )表示的是一个函数指针,首先p与结合,说明p是一个指针,然后再跟(int)结合,说明指针指向的是一个函数,最后与int结合,说明所指向的函数返回类型是int型。对于int (p)(int),指针类型是int ()( int ),指针所指向的数据int ()(int),即带有一个int参数、返回值为int 的函数。
对于一个指针,需要确定的是,指针类型、指针所指向的数据类型、指针本身的值、存放指针的内存区。
指针类型
从上面分析来看对于一个简单的指针,其指针类型可以简单地认为就是指针定义中,把所定义的指针的变量名去掉就是指针类型:
如:
int *p; //p是定义的指针,指针类型是int *
int (*p)[3]; //p是定义的指针,指针类型是int (*)[3]
int (*p)(int); //p是定义的指针,指针类型是int (*)(3)
指针所指向数据类型
从定义上来看,指针所指向的数据类型也可以简单的从定义上得到:直接把指针变量名和 * 声明符去掉:
如:
int *p; //p是定义的指针,指针所指向数据类型是int
int (*p)[3]; //p是定义的指针,指针所指向数据类型是int ()[3]
int (*p)(int); //p是定义的指针,指针所指向数据类型是int ()(3)
指针本身的值
也就是p本身的值,即p中存放的值。其中存放的值是指针p所指向的数据的存放地址,或者说是指针p所指向的内存区,即p本身的值是一个地址值,该地址中存放着p所指向的数据内容也就是*p,*p操作是取出该地址中的值,也就是指针p所指向的东西。
存放指针的内存区(指针的地址)
由定义可知,该部分是存放着指针本身,即指针所在的内存区,&p可以取到指针p所在的内存区的地址。当声明一个指针的时候,就意味着给指针本身分配了空间,但是此时指针所指向的空间则可能是无效的。
指针的初始化和赋值
首先,在对变量进行初始化之前,应该先明确变量是什么。对于指针而言,如上面所介绍的,指针p所存储的是所指向的数据在内存中的地址,而不是数据本身。清除了这一点,那么在指针进行初始化的时候,显然,初始化或者赋值的右值应该是“地址值”。
如:
int a=3;
int *p=&a;
此处,对于整型变量a,通过操作符&对a进行取地址操作,然后将该地址赋给指针p,这样p中便存储了变量a的地址(首地址)。而实际上的操作是,因为p是指针,所以其右值也应该是指针,也就是说,实际上&a的结果得到的是一个指针,指针类型就是a的类型加上*,而指针所指向的地址就是a的地址,然后用该指针对指针p进行赋值。
指针作为参数
一级指针和二级指针
当一级指针作为参数传入函数中,其主要目的通常是为了修改指针所指向的内容:
int a = 10;
int b = 20;
void func(int* p)
{
*p = 11;
p = &b;
}
void func2(int** p)
{
**p = 12;
*p = &b;
**p = 21
}
void main(int argc,char** argv)
{
int* p = &a;
printf("&a:%0x,a:%d,&b:%0x,b:%d,&p:%x,p:%0x,*p:%d\n",&a,a,&b,b,&p,p,*p);
func(p);
pprintf("&a:%0x,a:%d,&b:%0x,b:%d,&p:%x,p:%0x,*p:%d\n",&a,a,&b,b,&p,p,*p);
func2(&p);
printf("&a:%0x,a:%d,&b:%0x,b:%d,&p:%x,p:%0x,*p:%d\n",&a,a,&b,b,&p,p,*p);
}
输出为:
&a:601040,a:10,&b:601044,b:20,&p:d2a19a50,p:601040,*p:10
&a:601040,a:11,&b:601044,b:20,&p:d2a19a50,p:601040,*p:11
&a:601040,a:12,&b:601044,b:21,&p:d2a19a50,p:601044,*p:21
对与传入一级指针参数而言,在函数中进行了一个拷贝,即func中多了一个指针的拷贝,而该拷贝的指针与传入的指针一样是指向a,但是实际上不是同一个指针,可以通过该指针来改变所指向内存(a)的值,而在func中改变了指针指向(指向b),实际上只是改变了内部指针的指向,但是外部指针指向并没有改变(仍然指向a);
而二级指针,实际是指向指针的指针,即内部指针实际上是指向外部指针的,因而需要通过双重解引用的方式来获取a的值,也可通过该方式来改变a的值,而通过单层解引用的方式来获取外部指针,同时也能通过该方式来修改外部指针的值,也就是修改了外部指针的指向。在func2中,**p = &b,实际上改变了内部指针所指向内容的值,即外部指针,从而达到修改外部指针指向的目的。