C复杂声明解析.docx

上传人:b****6 文档编号:2921044 上传时间:2022-11-16 格式:DOCX 页数:18 大小:27.28KB
下载 相关 举报
C复杂声明解析.docx_第1页
第1页 / 共18页
C复杂声明解析.docx_第2页
第2页 / 共18页
C复杂声明解析.docx_第3页
第3页 / 共18页
C复杂声明解析.docx_第4页
第4页 / 共18页
C复杂声明解析.docx_第5页
第5页 / 共18页
点击查看更多>>
下载资源
资源描述

C复杂声明解析.docx

《C复杂声明解析.docx》由会员分享,可在线阅读,更多相关《C复杂声明解析.docx(18页珍藏版)》请在冰豆网上搜索。

C复杂声明解析.docx

C复杂声明解析

C复杂声明解析

2009-12-2023:

07258人阅读评论(0)收藏举报

creturningpointersfunctionreferenceparameters

C语言所有复杂的指针声明,都是由各种声明嵌套构成的。

如何解读复杂指针声明呢?

右左法则是一个既著名又常用的方法。

不过,右左法则其实并不是C标准里面的内容,它是从C标准的声明规定中归纳出来的方法。

C标准的声明规则,是用来解决如何创建声明的,而右左法则是用来解决如何辩识一个声明的,两者可以说是相反的。

右左法则的英文原文是这样说的:

Theright-leftrule:

Startreadingthedeclarationfromtheinnermostparentheses,goright,andthengoleft.Whenyouencounterparentheses,thedirectionshouldbereversed.Onceeverythingintheparentheseshasbeenparsed,jumpoutofit.Continuetillthewholedeclarationhasbeenparsed.

这段英文的翻译如下:

右左法则:

首先从最里面的圆括号看起,然后往右看,再往左看。

每当遇到圆括号时,就应该掉转阅读方向。

一旦解析完圆括号里面所有的东西,就跳出圆括号。

重复这个过程直到整个声明解析完毕。

笔者要对这个法则进行一个小小的修正,应该是从未定义的标识符开始阅读,而不是从括号读起,之所以是未定义的标识符,是因为一个声明里面可能有多个标识符,但未定义的标识符只会有一个。

现在通过一些例子来讨论右左法则的应用,先从最简单的开始,逐步加深:

int(*func)(int*p);

首先找到那个未定义的标识符,就是func,它的外面有一对圆括号,而且左边是一个*号,这说明func是一个指针,然后跳出这个圆括号,先看右边,也是一个圆括号,这说明(*func)是一个函数,而func是一个指向这类函数的指针,就是一个函数指针,这类函数具有int*类型的形参,返回值类型是int。

int(*func)(int*p,int(*f)(int*));

func被一对括号包含,且左边有一个*号,说明func是一个指针,跳出括号,右边也有个括号,那么func是一个指向函数的指针,这类函数具有int*和int(*)(int*)这样的形参,返回值为int类型。

再来看一看func的形参int(*f)(int*),类似前面的解释,f也是一个函数指针,指向的函数具有int*类型的形参,返回值为int。

int(*func[5])(int*p);

func右边是一个[]运算符,说明func是一个具有5个元素的数组,func的左边有一个*,说明func的元素是指针,要注意这里的*不是修饰func的,而是修饰func[5]的,原因是[]运算符优先级比*高,func先跟[]结合,因此*修饰的是func[5]。

跳出这个括号,看右边,也是一对圆括号,说明func数组的元素是函数类型的指针,它所指向的函数具有int*类型的形参,返回值类型为int。

int(*(*func)[5])(int*p);

func被一个圆括号包含,左边又有一个*,那么func是一个指针,跳出括号,右边是一个[]运算符号,说明func是一个指向数组的指针,现在往左看,左边有一个*号,说明这个数组的元素是指针,再跳出括号,右边又有一个括号,说明这个数组的元素是指向函数的指针。

总结一下,就是:

func是一个指向数组的指针,这个数组的元素是函数指针,这些指针指向具有int*形参,返回值为int类型的函数。

int(*(*func)(int*p))[5];

func是一个函数指针,这类函数具有int*类型的形参,返回值是指向数组的指针,所指向的数组的元素是具有5个int元素的数组。

要注意有些复杂指针声明是非法的,例如:

intfunc(void)[5];

func是一个返回值为具有5个int元素的数组的函数。

但C语言的函数返回值不能为数组,这是因为如果允许函数返回值为数组,那么接收这个数组的内容的东西,也必须是一个数组,但C语言的数组名是一个右值,它不能作为左值来接收另一个数组,因此函数返回值不能为数组。

intfunc[5](void);

func是一个具有5个元素的数组,这个数组的元素都是函数。

这也是非法的,因为数组的元素除了类型必须一样外,每个元素所占用的内存空间也必须相同,显然函数是无法达到这个要求的,即使函数的类型一样,但函数所占用的空间通常是不相同的。

作为练习,下面列几个复杂指针声明给读者自己来解析,答案放在第十章里。

int(*(*func)[5][6])[7][8];

int(*(*(*func)(int*))[5])(int*);

int(*(*func[7][8][9])(int*))[5];

实际当中,需要声明一个复杂指针时,如果把整个声明写成上面所示的形式,对程序可读性是一大损害。

应该用typedef来对声明逐层分解,增强可读性,例如对于声明:

int(*(*func)(int*p))[5];

可以这样分解:

typedefint(*PARA)[5];

typedefPARA(*func)(int*);

这样就容易看得多了。

c言语的复杂类型声明

在c言语的复杂类型声明中,我们用到3个修饰符:

*、()、[]。

含义如下:

*暗示声明一个指针

()暗示声明一个函数

[]暗示声明一个数组

c语言允许我们一次使用多个上面所说明的修饰符来申明一个变量,这样我们可以构造出多种多样的类别来。

大家先看下面的例子:

intboard[8][8];//二维数组(一个8个元素的数组,元素内容还是数组)

int**p;//指向指针的指针(一个指针,内容还是一个指针)

int*array[10];//10个元素的数组,元素内容是指针。

int(*p)[10];//一个指针,指向含有10个元素的数组。

int*oof[3][4];//3个元素的数组,存放的内容是指针,指针分别指向4个元素的数组

int(*oof)[3][4];//一个指针指向3*4的数组。

看到这里,可能有人会想,你在说些什么哦,怎么看不明白,没有关系,看完下面的3条法则,你在来看上面的内容就很清晰了。

1:

离名字越近的修饰符优先级越高

2:

[],()优先级比*高

3:

用()括起来的表达式的优先级最高。

我用这些法则来解释上面提到的例子,请看

int*foo[3][4]

名字:

foo

*、[3]离名字一样近,而[4]离的比他们远,所以*、[3]的优先级比[4]高。

(法则1)

而[]的优先级比*高(法则2)

优先级关系如下:

[3]>*>[4]

所以int*foo[3][4]是一个3个元素的数组(第一个修饰符[3]),存放内容是指针(第二个修饰符号*),指针分别指向4个元素的数组(第三个修饰符[4])

int(*foo)[3][4]

名字:

foo

优先级关系如下:

(括号括起来的表达式优先级最高)

*>[3]>[4]

所以一个指针指向3*4的数组。

下面的内容留给大家去思考:

int*func()[3];

int*f[3]();

alongzhang

是这样的:

存储类别类型限定词类型标识符

这种说明会给人们一种暗示:

C语言的声明是静止的、死板的,什么声明都能够以这个为基础,往上一套就OK了。

事实真的如此吗?

说句心里话,笔者也祈祷事实真的如此,这样世界就简单多了、清静多了。

但别忘了,这个世界总是让人事与愿违的。

实际上,C的声明的组织形式是以嵌套为基础的,是用嵌套声明组织起来的,并非象上面所述那么死板,存储类说明符一定得放在限定词前面吗?

类型说明符一定要紧贴标识符吗?

不!

C标准从来没有这样说过!

下面来看一看C89对声明的形式是如何规定的:

声明:

声明说明符初始化声明符表opt[opt的意思是option,可选]

其中声明说明符由以下三项构成:

声明说明符:

存储类说明符声明说明符opt

类型说明符声明说明符opt

类型限定符声明说明符opt

在这里,一个声明说明符可以包含另一个声明说明符,这就是声明的嵌套,这种嵌套贯穿于整个声明之中,今天我们看来一个非常简单的声明,其实就是由多个声明嵌套组成的,例如:

staticconstinti=10,j=20,k=30;

变量i前面就是声明说明符部分,有三个声明说明符:

staticconstint,static是一个存储类说明符,它属于这种形式:

static声明说明符

static后面的声明说明符就是constint,const是一个类型限定符,这也是个嵌套,它是由

const声明说明符

组成,最后的int是一个类型说明符,到这里已经没有嵌套了,int就是最底的一层。

对于存储类说明符、类型说明符和类型限定符的排列顺序,C标准并没有规定其顺序,谁嵌套谁都可以。

换言之,上面的声明可以写成:

intstaticconsti=10,j=20,k=30;或者constintstatici=10,j=20,k=30;

这无所谓,跟原声明是一样的。

再举一个有趣的例子:

constint*p;与intconst*p;

有些人会对后面一种形式感到困惑,因为他一直以来学习的都是那种死板的形式,因此他无法理解为什么那个const可以放在int的后面。

实际上对于标准来说,这是再正常不过的行为了。

上面举的例子是变量的声明,函数的声明也同样道理,例如:

staticconstintfunc(void);

......

intmain(void)

{

intstaticconst(*p)(void);

p=func;

.........

return0;

}

constintstaticfunc(void)

{

.......

return0;

}

func的函数原型声明、函数定义跟main内的函数指针p的声明是一样的。

但是,笔者并非鼓励大家把声明说明符写得乱七八糟,作为一个良好的风格,应该按照已经习惯约定的方式排列说明符,但懂得其中的原理非常重要。

声明staticconstinti=10,j=20,k=30;的int后面的部分就是初始化声明符表,这比较容易理解,这个符表实际上也是嵌套的:

初始化声明符表:

初始化声明符

初始化声明符表,初始化声明符

初始化声明符:

声明符

声明符=初值

声明符是初始化声明符的主体,现在来讨论一下声明符是如何规定的:

声明符:

指针opt直接声明符

这里写的指针opt指的是那个指针声明符*,要注意的是,*属于声明符,而不是声明说明符的一部分。

指针opt又包含:

指针:

*类型限定符表opt

*类型限定符表opt指针

在这里有一个常见的问题,就是constint*p;与int*constp的区别,第一个声明的const属于声明说明符,它跟int一起,是用来说明*p这个声明符的,因此const修饰的是p所指向的那个对象,这个对象是const的。

而第二个声明的const是声明符的一部分,它修饰的对象是p本身,因此p是const的。

上面规定的第二条值得注意,这

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 表格模板 > 调查报告

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1