| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 5251 人关注过本帖, 12 人收藏
标题:关于理解复杂指针声明及定义的看法。
只看楼主 加入收藏
lz1091914999
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:四川
等 级:贵宾
威 望:37
帖 子:2011
专家分:5959
注 册:2010-11-1
结帖率:94.64%
收藏(12)
已结贴  问题点数:100 回复次数:59 
关于理解复杂指针声明及定义的看法。
对于一个指针变量来说,最重要的是它指向的类型,它决定了编译器对它指向对象的理解,如:
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
pangding
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:北京
等 级:贵宾
威 望:94
帖 子:6784
专家分:16751
注 册:2008-12-20
收藏
得分:10 
这觉得很多讲到的指针实际很难用到。
返回数组的函数其实一般就不多见了,有这种需要一般就是返回个指针。
如果需要返回的东西其实是个函数指针,那么毫无疑问应该强制使用 typedef 来化简代码。

有关指针,我见过最经典的解释就是《C陷阱与缺陷》里面的。
其实要想使指针的声明变复杂,最简明的方便就是使它变成多级指针,并且中间包含数组和函数。

百分帖很诱人,但我觉得我能答对最后的那个问题。所以还是先不答了。
2012-07-22 22:53
obstratiker
Rank: 8Rank: 8
等 级:蝙蝠侠
威 望:1
帖 子:198
专家分:758
注 册:2011-5-5
收藏
得分:10 
ok,照楼主的分析试试
首先 p 是一个指针,这个指针指向大小为 [10] 的数组,并且数组的每个元素都是指针,这些指针指向一个函数,函数接收一个 int 的变量,这个函数返回一个指针,而这个指针又指向一个函数,该函数接收两个 int 变量,并且返回一个指向 int 型变量的指针
2012-07-22 23:09
信箱有效
Rank: 13Rank: 13Rank: 13Rank: 13
等 级:蒙面侠
威 望:9
帖 子:1102
专家分:4268
注 册:2012-6-19
收藏
得分:10 
我觉得我无法答对最后的那个问题。所以还是不答了。
2012-07-22 23:13
小习小习
Rank: 13Rank: 13Rank: 13Rank: 13
等 级:蒙面侠
威 望:6
帖 子:1467
专家分:4792
注 册:2012-7-2
收藏
得分:10 
回复 楼主 lz1091914999
P指向有10个元数指针数组,该数组的每一个元数指向两个参数为int.返回值为指向函数的指针,该指针指向含有两个int参数,返回值为一个指向int的指针。版主这样对了吗?

实现自己既定的目标,必须能耐得住寂寞单干。
2012-07-22 23:30
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
小习小习
Rank: 13Rank: 13Rank: 13Rank: 13
等 级:蒙面侠
威 望:6
帖 子:1467
专家分:4792
注 册:2012-7-2
收藏
得分:0 
以下是引用小习小习在2012-7-22 23:30:02的发言:

P指向有10个元数指针数组,该数组的每一个元数指向两个参数为int.返回值为指向函数的指针,该指针指向含有两个int参数,返回值为一个指向int的指针。版主这样对了吗?

前面是接收一个int.粗心了

实现自己既定的目标,必须能耐得住寂寞单干。
2012-07-22 23:34
beyondyf
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:103
帖 子:3282
专家分:12654
注 册:2008-1-21
收藏
得分:10 
怎么没解释一下 const int * 与 int const * 的区别

重剑无锋,大巧不工
2012-07-23 00:04
demonleer
Rank: 9Rank: 9Rank: 9
等 级:贵宾
威 望:10
帖 子:483
专家分:1225
注 册:2012-6-4
收藏
得分:10 
纯属凑热闹接分,哼哼~
2012-07-23 09:09
qq872551969
Rank: 9Rank: 9Rank: 9
等 级:禁止访问
威 望:1
帖 子:241
专家分:1377
注 册:2012-7-13
收藏
得分:10 
真的看不懂。

编程交流请加群:【234181324】,一起学习,一起进步,新建的群,主打C语言和JAVA等程序设计,等待高手们的入驻,无论你是高手也好,新手也好,在这里都是平等的,欢迎你们的加入~!【234181324】
2012-07-23 09:16
快速回复:关于理解复杂指针声明及定义的看法。
数据加载中...
 
   



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

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