下午突然发愤决定搞定复杂指针的相关问题,看了几篇大大们的文章后,感觉略有收获。
个人的总结:从非保留字读起,如果有多个非保留字(如函数指针中的形参名,虽然这并没有必要),从最左边的读起。此后,如果遇到右括号,则向左读。如果遇到左括号,则向右读,读完所有括号后再按优先级分析一次。
我们来试一试,我会说的详细些。
int *(*(*func)(void *))[5];
只有一个非保留字func,从它读起。
声明一个叫func的东西。
遇到右括号,向左读。
这个func是一个指针。
遇到左括号,向右读,跳过已经被分析的部分,遇到左括号,继续向右。
这个指针指向一个函数,函数接受void指针。
读到右括号,向左读。
函数返回一个指针。
此时所有的括号都分析完成,剩下的就很简单了。
[5]说明这个指针指向一个含有5个元素的数组。
*说明这个数组内的元素是指针。
最后int说明指针指向int数据类型。
最后总结:func是一个指向函数的指针,函数接受void指针,返回指向数组的指针,数组由5个int指针组成。
如果没晕的话,可以自行尝试下面的声明。答案可以用小刀用力刮开。
int (*(*func)[5])(void *);
func是一个指针,指向一个有5个元素的数组,每一个元素都是一个指向函数的指针,函数接受void型指针,返回一个int值。
声明理解的差不多了,那怎么调用呢?
#include<cstdio>
using namespace std;
int (*(*func)[5])(void *);
int a(void *pa){
return 0;
}
int b(void *pb){
return 1;
}
int c(void *pc){
return 2;
}
int d(void *pd){
return 3;
}
int e(void *pd){
return 4;
}
int main()
{
(*func)[0]=a;
(*func)[1]=b;
(*func)[2]=c;
(*func)[3]=d;
(*func)[4]=e;
int *pn;
int ans;
int h=20;
pn=&h;
ans = (*func)[3]((int *)pn);
printf("%d",ans);
return 0;
}
既然func是一个指向数组的指针,那如果想操作数组内的元素,当然要先用"*"将func退成数组。
然后,再加上下标,同时用括号更改优先级,就变成了一个个指向函数的指针,我们再赋给它函数的地址,就能调用了
按照我的预期,它应该输出3。
不过呢,发现程序崩溃了。
debug发现程序连第一步都没走完。
不由得怀疑自己是不是完全理解错了。于是在watches中查找对应的数据
重新读了一下自己的程序,发现问题了……
func是【大段定语】指针啊!
不管定语有多长它都是个指针啊!!
没初始化地址啊!!!
野指针啊!!!!
由于隐式初始化的关系,倒也谈不上危险,不过无法运行是肯定的了。改进如下。
#include<cstdio>
using namespace std;
int (*(*pfunc)[5])(void*);
int a(void *pa){
return 0;
}
int b(void *pb){
return 1;
}
int c(void *pc){
return 2;
}
int d(void *pd){
return 3;
}
int e(void *pd){
return 4;
}
int main()
{
int (*func[5])(void*);
pfunc=&func;
func[0]=a;
func[1]=b;
func[2]=c;
(*pfunc)[3]=d;
(*pfunc)[4]=e;
int *pn;
int ans;
int h=20;
pn=&h;
ans = (*(*pfunc)[3])((int *)pn);
printf("%d",ans);
return 0;
}
取这个数组的地址赋给指针。
再把数组内元素分别赋以函数地址,这里分别用直接法和间接法赋地址。
这样就不会出事故了。
总之是千万要注意野指针。