数据类型的混和搭配使用,让人郁闷不已。。比如说:结构指针,数组指针,指针数组,函数指针,指针函数。。这些朦胧的概念让人抓狂不已。。。因为有了这样的复杂性,不只使指针本身变得复杂,更让人头痛的是和指针扯上了关系的声明,因为声明和定义应该是严格配对的,更进一步的说,C中的左值和右值的数据类型应该是严格配对的,只要有一点儿偏差,在编译的时候我们就不要指望我们的程序编译通过。或许int *a; 这样的声明是简单的,但是下面这个声明:
int *(*(*(*f)())[6])()
可能就不是那么容易理解了。。那么这句是什么意思?他表示:
“指向‘返回值为int型指针的函数指针’的数组指针”的函数指针。
说真的,这是一个非常让人难以理解的概念。。但是要想真正搞明白他,我们要从两方面入手,第一:最基本的数组指针,指针数组,函数指针,指针函数概念的建立。第二:优先级和结合型。
为了搞明白这个问题,让我们先来看下面的声明:
Int *p;
这个很好理解,说的是p是一个指向int型数据的指针。当然,如果我们换一种写法,这个意思会更明显 int* p;这就说明p是一个int型的指针。但是一般不建议这种写法,为什么?比如下面这句:int* p,p1;这样会给人一样误解,以为p和p1都是一个int型的指针。但是事实上p1是一个int型的数据。
再看下面这个声明:int f();很显然,这说的一个函数,他返回了一个int型的数据。再来:int *f()。这个问题很迷惑人,我们要怎么把f同*和()相结合呢?情况是这样的,*是一个左目运算符,他从右向左结合,但是他的优先级很低,所以f会先和()相结合,意思就是说f是一个函数,然后我们再向左看,这个函数有返回值,返回了什么呢?对,返回了一个int型的指针。所以这一句描述的是一个返回int型指针的函数。注意这句话的最后两个字“函数”。我们平时说压轴的总是最后出场,在这句里函数才是老大。总的来说,这句描述的是一个指针函数。
大家再看,如果我们把上面这句稍加包装,又是一个什么效果呢:int (*f)();
这下意思可就完全变了。虽然*的优先级很低,但是现在有()在给他撑腰,他就气势如虹的把f给抢过来了。使f变成了一个指针。这个指针指到了哪里呢?看后面()。说明这个指针指向了一个函数。那前面那个int说明一个什么问题呢?int说明这个指针指向的函数返回了一个int型的数组。现在我们连起来读:f是一个指向返回为int型数据的函数指针。还是那句话:压轴的是老大。所以,这句描述的是一个指针。因为他指向了函数,所以说我们给他起了一个名字:函数指针。
说到指针,我们不得不提到数组。因为他和指针关系千丝万缕。。以至于我们经常把他们搞混。我们可以这样定义一个数组 int a[5]={1,2,3,4,5};如果我们要访问这个数组的一个元素如a[2],也就是第三个元素(因为数组元素是从0开始计数)。我们可以这样写:int Element=a[2];那到这句话在内存中是怎么实现的呢?我们知道一个一维数组的数组名代表了这个数组的首地址。也就是第0个元素的地址。程序在执行这句的时候,他会或得a的值,也就是一个数组的首地址,然后和[]这个下标运算符里的偏移量运算相加,得到一个新的地址,然后再取得这个地址里的内容。比如我们设a为0x0000fff0;这是一个地址,然后如果我们想取得a[2]的值的话,cpu会把得到这样一个新地址:0x0000fff0+2*4,也就是0x0000fff8,到这这个地址里去取a[2]的值。为什么我们要2*4?因为int型的数据是32位的(当然,不同的编译器想法不一样,也可能是16位的。)但是我们一个地址里只有有8位数据,也就是一个Byte,所以4个地址才可以装下一个int型数组。这样我们可以看出,一维数组名其实是一个指针,只不过他不是指针变量,而是一个常量。属性为只读。任何对企图对他的改都是犯法的,比如说对数组名自加:a++;但是编译器会给你报出如下错误: must be a modifiable lvalue。意思就是说这个表达是必须是一个可修改的值。那么如果我们想通过指针的方法怎么来实现对数组的访问呢?我们可以设一个指针 int *ptr;ptr就是一个指针变量,然后ptr=a;这样就把数组的首地址给了ptr这个指针变量。因为他是一个变量,对ptr的修改是允许的,所以我们可以执行int a=*ptr++;这样我们就实现了对数组a用指针的形式的访问。那么指针和数组有什么区别?区别有以下几点:1,指针存放的是数据的地址,而数组存放的是数据;2,指针是一种间接访问数据的机制,首先取得指针的内容,把它作为地址,然后从这个地址提取数据。如果指针有一个下标[],就把指针的内容加上下标中的偏移量,从中提取数据。但是数组是直接访问数据,a只是简单的以a为首地址加上i这个偏移量做为新的地址去取数据。3:指针通常用于动态的 数据结构。而数组常用于存储固定数目且数据类型相同的元素。4:指针通常指向匿名的数据。而数组名却指向自身。
数组,指针;指针,数组;当他们两个相遇的时候,他们就已经难分难舍了。下面的两个声明就可以做为见证:
int *a[]; int (*a)[];
初看之下,可能没有多大感觉,但是他们所表达的意思却是失之毫厘,差之千里。我们先来看第一个:int *a[];我们最关注的是他的结合方式,从上面我们知道*的优先级是很低的,所以a会先和[]结合,说明a是一个数组,这个数组里是什么东西呢?int* 。int型的指针。所以说a是一个数组,数组里的每个元素都是指向int型数据的指针。我们把他简称为指针数组。注意,这个声明描述的是一个数组。
然后我们来看int (*a)[]。虽然*的优先级很低,但是有()在帮他,所以a先和*结合,说明a是一个指针,指到了哪里呢?再看后面的[],说明他指向了数组。而且这个数组里的每一个元素都是int型的。连起来读,说的就是:a是一个指向int型数组的指针。也就是数组指针。
到此为止,我们明白了函数指针和指针函数,数组指针和指针数组的区别。那到现在我们可以进一步的去探索了。我们已经知道 int (*m)();说的是一个函数指针,int *m();说的是一个指针函数,那么,我们如何声明一个函数指针去调用一个指针函数呢?我们可以这样做:int*(*m)();首先我们应该知道m是一个指针,因为*的优先级很低,所以m这个指针和()先结合,说明这个指针指向了一个函数,然后我们再和前面那个int*结合,说明这个函数返回了一个指针。这样我们这个声明的意思就出来了:m是一个指向返回int型指针的函数指针。好了,现在我们开动,来分析int *(*(*(*f)())[6])()
这句。首先,我们可以先去掉他那一身把他包得严严实实的外衣。好把这个这个声明看清楚。首先,(*f)()说明f是一个函数指针,*(*f)()说明这个函数指针指向的函数返回了一个指针,但是这个指针指到了哪里呢?我们发现外面这一层含有*和[6],而且没有括号,因为[]的优先级比*高所以说,返回的这个指针会和[6]结合,说明他指向了数组,现在再来和前面的*结合,说明指向的这个数组是一个指针数组,现在我们把(*(*(*f)())[6])分析出来了。那这个指针数组又指到了哪里呢?现在我们只剩下前面的*和后面的(),当然,这个指针先和()结合,说明他指向了一个函数,最后我们再看前面的int *,说明这个函数返回了一个int型的指针。
OK,大功告成。现在我们把上面的逻辑联系起来,int *(*(*(*f)())[6])()是:
“指向‘返回值为int型指针的函数指针’的数组指针”的函数指针。
其实还是够呛的。。。但是只要我们一层一层的去分析,总可以得到我们想要的结果。理论说了一大篇,现在我们看下实际的一个代码吧。这样更有利于理解:
#include<stdio.h>
int (*func(int* a1,int* a2, int array3[10]))[2];//声明这个函数
int array1[]={1,2,3,4,5,6,7,8,9,10};
int array2[]={10,9,8,7,6,5,4,3,2,1};
int array3[10];
void main()
{
int *arrptr;
//声明一个数组指针,指向二维整型数组
int (*p)[2],h;
//声明一个函数指针,指向一个传入三个数组的函数,该函数返回一个指向数组的指针
int (*(*m)(int*,int*, int []))[2];
m=func; //让这个函数指针指向zheng这个函数
//把这个函数返回的数组指针赋给p这个数组指针
p=(*m)(array1,array2,array3);
//输出二维数组每行的首元素
printf("the first element of each line in the Two-dimensional array is:\n ");
for(h=0;h<2;h++)
{
printf("%d ",*(*p));
p++;
while(h==1)
{
break;
}
}
//让arrptr指向array1这个这个数组
arrptr=array1;
printf("\n第一个数组为:\n");
for(h=0;h<10;h++)
{
printf(" %d",*arrptr++);
}
//让arrptr指向array2这个这个数组
arrptr=array2;
printf("\n第二个数组为:\n");
for(h=0;h<10;h++)
{
printf(" %d",*arrptr++);
}
//让arrptr指向array3这个这个数组
arrptr=array3;
printf("\n两个数组的积为:\n");
for(h=0;h<10;h++)
{
printf(" %d",*arrptr++);
}
printf("\n\n hello world! i get success!!!\n");
}
//定义一个指针函数,传入三个数组,返回一个指向整形数组的指针
int (*func(int* a1,int* a2, int array3[10]))[2]
{
int h;
int (*ptr)[2];
static int c[2][2]={{1,2},{3,4}};
for(h=0;h<10;h++)
{
*array3++ = *a1++ * *a2++ ;
}
ptr=c;
return ptr;
}
这个程序的主要路线就是用函数指针去调用了一个返回了数组指针的函数。整个调用过程大家可以去VC上调一下。观察各个变量的变化,我相信很快就可以搞明白。由于自己本身水平有限,还望大家多多指正,共同交流,共同学习.
|