| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 5251 人关注过本帖, 12 人收藏
标题:关于理解复杂指针声明及定义的看法。
取消只看楼主 加入收藏
lz1091914999
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:四川
等 级:贵宾
威 望:37
帖 子:2011
专家分:5959
注 册:2010-11-1
结帖率:94.64%
收藏(12)
已结贴  问题点数:100 回复次数:7 
关于理解复杂指针声明及定义的看法。
对于一个指针变量来说,最重要的是它指向的类型,它决定了编译器对它指向对象的理解,如:
int i;
int* p = &i;
p现在指向一个int大小的内存空间,并且这个空间是int变量i的空间,那么我们就可以通过p来间接的读取或修改i的值,如:
int j = *p;
*p = 1;
*p是规定的语法,也可以说在这里*p就与i等价。这是因为编译器已经拥有了足够的信息,它知道p指向的空间大小(一个int的大小)及对这块空间里值的解释(补码)。
再看一看这段代码:
程序代码:
struct Book {
    char name[31];
    char author[31];
    char isbn10[11];
    char isbn13[14];
    char language[100];
    char publisher[100];
};

struct Book thinking_in_cpp;
struct Book* p = &thinking_in_cpp;

strcpy(p->name, "Thinking in C++");
strcpy(p->author, "Bruce Eckel");
strcpy(p->isbn10, "0139798099");
strcpy(p->isbn13, "9780139798092");
...

printf("Book name: %s\n", p->name);
printf("Book author: %s\n", p->author);
printf("Book isbn-10: %s\n", p->isbn10);
printf("Book isbn-13: %s\n", p->isbn13);
...
可以看到,这里的p指向的并不是基本类型,它指向的是我们自定义的结构类型Book,当然编译器在struct Book* p = &thinking_in_cpp;这段代码之后就已经了解到了足够的信息,编译器知道p指向的空间有多大(使该指针+1或-1会增加或减少sizeof(struct Book)),还知道p指向的是一个结构体变量,它有6个成员,并且还可以使用->操作符来引用该结构变量的成员。

好了,这说明指针需要的不仅仅是地址,而更重要的是它指向的类型(编译器可以允许使用的操作符及对其值的解释)。
下面有一些变量的定义:
程序代码:
int** p;                  // p指向一个int*的变量,它是指向指针的指针。
void(*p)(void);           // p指向一个函数,该函数不接收参数,也不返回值。
int(*p)(int,int);         // p指向一个函数,该函数接收两个int参数,并且返回一个int。
void(**p)(void);          // p指向一个函数的指针,它指向的指针指向的函数不接收参数也不返回值,它是指向指针的指针。
void(*p[10])(void);       // p是一个数组,每个元素都是一个指针,并且每个元素都指向一个函数,这个函数不接收参数也不返回值。
void(*(*p)[10])(void);    // p是一个指针,它指向一个有10个元素的数组,这个数组的每个元素都是一个指针,并且每个元素都指向一个不接收参数也不返回值的函数。
int(*(*p)(void))[10];     // p是一个指针,它指向一个函数,这个函数不接收参数,并且返回一个指针,这个指针指向10个int的数组。
void(*(*p)(void))(void);  // p是一个指针,它指向一个函数,这个函数不接收参数,并且返回一个指针,这个指针也指向一个函数,这个函数不接收参数,也不返回值。
对于void (*p)(void);这是函数指针的语法,*p两边的括号是不能少的,比如:void *p(void);p是一个函数,这个函数不接收参数,返回一个void*指针,并且这是一个函数的声明。加上了括号就使编译器先看到了*p(这说明()能改变声明及定义时的优先级规则,否则编译器就是从右往左来识别变量的类型),这样p就是一个指针,然后右边发现了(void),这就可以表示p是一个函数指针了,因为遇到了一对括号,并且这个括号里有东西,括号里面是void,则说明指向的这个函数不接收参数,最后在左边发现了void,说明指向的这个函数不返回值。

在声明或定义时,编译器是从标识符开始的,先看标识符的右边,然后左边,然后是上次右边的右边,然后是上次左边的左边。。。以此类推。那么我们来看看这个:
int(*p)(int,int);先从标示符开始,发现*p被括号包起来了,所以p先与*结合,p就变成了一个指针(说明()改变了优先级),然后再看右边发现了(int,int)这说明p是一个函数指针,指向的这个函数接收两个int参数,最后再看左边,发现了int,说明这个函数返回一个int。

看看下面的一些解释:
void(**p)(void);先从标示符开始,很明显**p被括起来了,那么p就是一个指针并且它指向一个指针(指针的指针),然后在右边发现了(void)这说明p指向的指针是一个函数指针,这个函数不接收参数,最后在左边发现void,说明p指向的指针指向的函数不返回值。所以p就是一个指向函数指针的指针。

void(*p[10])(void);先从标示符开始,*p[10]被括起来了,那么先看p的右边,发现了[10],说明p是一个数组并且这个数组有10个元素,然后在左边发现了*,说明这个数组的每个元素都是一个指针,然后再看右边,发现了(void),说明这个数组的每个元素都是一个函数指针并且这个函数不接收参数,最后在左边发现了void,说明这个函数不返回值。所以p实际上是一个函数指针的数组。

void(*(*p)[10])(void);先从标示符开始,*p被括起来了,那么p就与*结合,所以p就是一个指针,然后在右边发现了[10],说明p指向一个有10个元素的数组,然后在左边发现了*,说明这个数组的每个元素都是指针,然后再右边发现了(void),说明这个数组的每个元素都指向一个函数并且这个函数不接收参数,最后在左边发现了void,说明这个函数不返回值。所以p是一个有10个函数指针数组的指针。

int(*(*p)(void))[10];先从标示符开始,*p被括起来了,那么p就与*结合,所以p就是一个指针,然后在右边发现了(void),说明p是一个函数指针并且这个函数不接收参数,然后再左边发现了*,说明p指向的这个函数返回一个指针,然后再右边发现了[10],说明这个函数返回的指针指向一个有10个元素的数组,最后在左边发现了int说明这个数组的每个元素都是int。所以p是一个函数指针,并且这个函数返回的是一个指向有10个元素的数组的指针。

void(*(*p)(void))(void);依然先从标示符开始,*p被括起来了,那么p就与*结合,所以p就是一个指针,然后在右边发现了(void),说明p是一个函数指针并且这个函数不接收参数,然后再左边发现了*,说明这个函数返回一个指针,然后在右边又发现了(void)说明返回的这个指针是一个函数指针并且这个函数不接收参数,最后在左边发现了void,说明返回的函数指针指向的函数没有返回值。所以p就是一个函数指针,并且指向的这个函数也返回一个函数指针。

最后请大家来理解一下,下面这句定义(回答正确的童鞋都有分):
int*(*(*(*p)[10])(int))(int,int);

[ 本帖最后由 lz1091914999 于 2013-1-10 11:54 编辑 ]
搜索更多相关主题的帖子: 编译器 
2012-07-22 22:37
lz1091914999
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:四川
等 级:贵宾
威 望:37
帖 子:2011
专家分:5959
注 册:2010-11-1
收藏
得分:0 
回复 2楼 pangding
确实很少用到,就算用到了,也基本上都是用typedef简化过的,不过从原始的角度上来理解可以消除许多的疑问。

My life is brilliant
2012-07-22 23:34
lz1091914999
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:四川
等 级:贵宾
威 望:37
帖 子:2011
专家分:5959
注 册:2010-11-1
收藏
得分:0 
回复 8楼 beyondyf
const int* p; 从标示符开始,p的右边已经没有类型了,左边发现了*,则p是一个指针,然后在左边发现了int,则它指向的类型是int,最后在左边发现了const,则p指向的这个int是只读的。p就是一个指向只读变量或常量的指针(Pointer to constant)。

int const* p; 从标示符开始,p的右边已经没有类型了,左边发现了*,则p是一个指针,然后在左边发现了const,则它指向的数据是只读的,最后在左边发现int,则它指向的这个只读数据的类型是int。所以这个p跟上面的p一样也是一个指向只读变量或常量的指针。

const int* const p; 从标示符开始,p的右边已经没有类型了,左边发现了const,则p是一个只读变量,然后在左边发现*,则p是一个指针(但它只能指向一个地方),然后在左边发现int,则它指向的数据的类型是int,最后在左边发现const,则它指向的是一个只读int或一个int常量。

int const* const p; 和上面的一样,const如果在*的左边,则代表这个指针指向一个不可改变的对象,在*号的右边则代表这个变量它自己是只读的。

My life is brilliant
2012-07-23 11:02
lz1091914999
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:四川
等 级:贵宾
威 望:37
帖 子:2011
专家分:5959
注 册:2010-11-1
收藏
得分:0 
以下是引用netlin在2012-7-24 09:50:29的发言:

谢谢楼主,提供这么一个学习讨论贴子!

3楼的表述很清晰,我也认同。

我来做点细化工作吧(只是我的理解,不知对不对?):
int*(*(*(*p)[10])(int))(int,int);
*p                                        p是一个指针变量。
(*p)[10]                                指向一个有10个元素的数组。
*(*p)[10]                                这10个元素都是指针。
(*(*p)[10])(int)                        这10个元素中的指针都指向函数,该函数接收一个int参数。
*(*(*p)[10])(int)                        函数返回int指针
(*(*(*p)[10])(int))(int,int)            函数返回指针指向另一函数,该函数接收两个int参数
int*(*(*(*p)[10])(int))(int,int)         函数返回int指针

只要认真学习楼主提供的关于指针的解读,再来分析楼主最后布置的作业是不难的!
感觉楼主象是在上一堂课,一个很有新意的网上课堂。

烦请楼主看看我的作业,看看我的理解对不对?
*(*(*p)[10])(int)                        函数返回一个指针
这里还不知道返回指针的类型。

My life is brilliant
2012-07-24 11:09
lz1091914999
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:四川
等 级:贵宾
威 望:37
帖 子:2011
专家分:5959
注 册:2010-11-1
收藏
得分:0 
回复 3楼 obstratiker
恩,正确。

My life is brilliant
2012-07-24 11:09
lz1091914999
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:四川
等 级:贵宾
威 望:37
帖 子:2011
专家分:5959
注 册:2010-11-1
收藏
得分:0 
回复 7楼 小习小习
你也正确 。

My life is brilliant
2012-07-24 11:10
lz1091914999
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:四川
等 级:贵宾
威 望:37
帖 子:2011
专家分:5959
注 册:2010-11-1
收藏
得分:0 
回复 35楼 wsws23
const和type两者的顺序并不影响什么。

My life is brilliant
2013-03-02 11:51
lz1091914999
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:四川
等 级:贵宾
威 望:37
帖 子:2011
专家分:5959
注 册:2010-11-1
收藏
得分:0 
回复 36楼 梦幻乐园
呵呵,好好学习吧!

My life is brilliant
2013-03-02 11:52
快速回复:关于理解复杂指针声明及定义的看法。
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.014419 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved