说到C语言的指针,可能大家都比较熟悉,基本上的都知道,指针就是存放地址的,无论是普通变量的地址,还是函数的地址都可以用指针表示。如果该指针用一个变量表示,那么这就是一个指针变量。所为指针变量就是这个指针所存放的地址是可以发生变化的,例如
int i = 6;
int j = 7;
int* p;
p = &i;
p = &j;
由上面的代码可以看出,首先int类型的指针p是指向变量i的地址的,但是后来指针p又指向了变量j的地址,因此我们就说p是一个int类型的指针变量。
现在我们知道了什么是指针变量了,那什么又是指针类型呢?像我们上面讲的p是一个int类型的指针变量,还有char型指针变量、函数指针等等。这都说明了指针也是有类型的。可能我们会想,指针不就是存放地址吗,地址的长度不都是一样吗,要么16位,要么就是32位,或者是64位。那既然不同类型的变量其地址都是一样的,为什么还要给指针划分类型呢?
给指针划分类型主要有如下几点好处,我们都知道char型变量其大小都是一个字节的,即用一个字节就可以表示一个char类型的变量。如果我们定义一个char类型的指针,那么该指针就是指向一个字节的地址。指针加一指向的地址便是下一个字节的地址。
char a = 'b';
char* p ;
p=&a;
//若a的地址是:0x01,那么p+1所指向的地址就是0x02。
同样,如果是int类型的话,那么指针p就是指向两个字节的地址。
理解好了上面的概念之后,这里我想着重的讲解一下几个指针中难以理解的知识点。
数组指针
表示一个指向数组的指针。//int类型数组指针
int (*p) [5];
//char类数组指针
char (*p) [7];
首先,由于指针*p被括号括起来了,说明这是一个指针,只不过该指针指向一个一维数组,同时改一维数组的长度为5或7,若int (*p)[5];
则p指向的是0x00;那么p+1则指向的是0x0a;因为int类型由两个字节表示,同时由于p表示指向一个int类型的一维数组,该一维数组的长度为5。那么p+1则表示向后推移的第10个字节。一般数组指针用来指向一个二维数组的。例如:
int a[3][4]={1,2,3,4,5,6,7,8,9,11,1,2};
int (*p)[4],i=2,j=3;
p=a;
printf("a[%d,%d]=%d\n",i,j, *(*(p+i)+j));
//输出为a[2,3]=2
注意:*(p+i)表示a[i][0]的地址,而不是a[ i ][ 0 ]的值。这是C语言规定的表示方式。
指针数组
表示一个存放指针的数组。 表示方式为:指针类型 * 指针名字[ 指针个数 ]
//int类型指针数组
int* p[5];
//char类型指针数组
char* p[7]; 或者 char* p [ ];
由于[ ]比*的优先级要高,因此 p 会先于 [ ] 结合,形成一个数组,然后才会与 * 结合表示数组类型为指针类型。指针数组广泛应用于C语言编写的程序当中,我们经常会在代码中看到这样一个程序。就拿一个非常简单的C语言的main函数来说。
int mian (int argc, char *argv[])
{
//代码
}
我们在mian函数的参数里面就使用了指针数组,这个指针数组的作用就是存放我们输入给mian函数的各个字符串的指针,因为main函数并不知道我们会输入多少个字符串给mian函数,所以在定义指针数组的时候,采用了不定义指针数组的个数的方式。
指针的指针
下面讲解C语言指针里面一个比较难以理解的概念,也就是指针的指针。何为指针的指针呢,顾名思义就是一个存放另一个指针地址的指针。表示方法是
指针类型 * * 指针名称 ;
例如:
int **p;//定义一个int类型的指针的指针。
在这里我想要说明的是,指针的指针与数组指针是有区别的,不能将二者混为一谈。我们为什么要提出指针的指针这一概念呢,回答这一问题,我首先要向大家举一个例子,例如当我们把一个指针作为参数传给一个方法时,其实只是将指针的副本传递给了方法,如果我们在方法中重新制定该参数的指向,并不能改变原来指针的指向。
int m_value = 1;
void func(int *p)
{
p = &m_value;
}
int main(int argc , char *argv [ ])
{
int n = 2;
int *pn = &n;
cout<<*pn<<end1;
func(pn);
cout<<*pn<<end1;
return 0;
}
//结果为
/**
2
2
*/
之所以结果是2 ,2 而不是2,1,是因为我们在mian函数中,虽然将指针作为*pn作为参数,传入func函数,但实际上是的传入过程是这样的
int *p = pn;
也就是说我们只是将pn所指向的地址赋给了指针p,但是由于指针p在func函数中又运行了
p = &m_value;
所以这个时候p的指向发生了变化,开始指向m_value了。但是pn的指针指向并不会因为p发生改变而改变,所以pn仍然是指向n。接下来我们看看如果将指针的指针作为函数参数的话,又会发生什么变化。
int m_value = 1;
void func(int **p)
{
*p = &m_value;
}
int main(int argc , char *argv [ ])
{
int n = 2;
int *pn = &n;
cout<<*pn<<end1;
func(&pn);
cout<<*pn<<end1;
return 0;
}
/**
结果为:
2
1
*/
这个时候我们是将pn指针的地址作为参数传给了指针的指针p,这个时候p是指向指针pn。而*p存放的是pn所指向值的地址,在传入的时候,应该是指向变量n的地址的。为了能够形象表示这三者之间的关系,我用一个图来表示。
这里需要注意的是*pn和*p并不是一回事,*pn和p才是一回事,都表示指向变量的地址,而**pn和*p才是一回事,表示所指向的变量的值。
函数指针
int (*p)(int,int);
只能指向返回值为int类型的,并且带有两个int参数的函数。
int (*p)(int, int );
//...............
int * p (int,int);
上面一种表示定义的是一个函数指针。而后面一种表示定义的是一个函数,其返回值为int类型的指针。其根本原因是由于()的优先级比*的优先级要高,所以p会先于右边的()先结合,表示一个函数。后面的int * 便表示返回值的类型了。