发博文
raof01-Haste makes waste

raof01.blog.chinaunix.net

WHAT?-HOW?-WHY?我真的是程序员吗?其实,我是个艺术家。   
个人资料
  • 博客访问:362444
  • 博文数量:203
  • 博客积分:9195
  • 博客等级:中将
  • 关注人气: 2
  • 注册时间:2006-01-06 16:08:20
订阅我的博客
  • 订阅
  • 订阅到鲜果
  • 订阅到抓虾
  • 订阅到Google
字体大小: 博文
分类: C/C++

这里说的声明,不光适用于C/C++,其他的一些语言也能适用。

与java和C#等不同,声明和定义在C/C++中有着比较明显的区别:声明仅仅是介绍名字(introduce names),而定义则会为该名字分配相应的空间。打个通俗的比喻:声明就是你在谈话中提到某个人的名字,而定义就是把你提到的这个人带到谈话的人群中来,让大家见识一下他/她是什么样子。

这里主要介绍声明。
在C中,声明的形式为(dcl是declaration的简写):
dcl: optional *'s direct-dcl(含有可选"*"的direct-dcl)
direct-dcl name
                (dcl)
                direct-dcl()
                direct-dcl[optional size]
根据该规则进行逆向解析,就可以得到正确的声明。简化一下:“TypeName Declarator;”其中,Declarator就是声明中的那个名字。当你遇到任何你不能理解的声明时,这个法则就是救命稻草。最简单的例子:
int aInt;
这里,int是TypeName,aInt是Declarator。
再说明一下结合紧密度。在声明/定义变量时,可以使用一些修饰比如“*”,“[]”,“()”等。“()”(非函数声明中的“()”)具有最高的紧密度,其次才是函数和数组的“()”和“[]”。
没有“*”的声明称为直接声明(direct-dcl),而有“*”称为声明(dcl)。直接声明要比声明结合的紧。分解声明时,先读出结合紧的。在这里,我把direct-dcl称为更紧的结合,它比dcl结合得紧。
最后,需要你用英语来读出这个声明。对于“[]”,应该读成array of。
对于复杂的定义,可以将其分解。比如“T (*p)()”可以分解成“T D1()”,D1读作:function returning T。其中D1是*p。那么该声明应该读成:p is a poniter to。二者合在一起,就变成了p is a pointer to function returning T,即:p是指向返回T类对象的函数的指针。

再看一个稍微复杂的示例:
T (*pfa[])();
根据dcl和direct-dcl,可以分解成T1 D1(因为结合紧密度),T1, 也就是T (),那么应该读作:
D1 is function returning T。
D1又可以写成T2 D2,其中T2是T1 [],可以分解成T1 D2[],读作:
array of D2 function returning T。
D2是指针,读作:pointers to。那么整个“T (*pfa[])();”应该读作:
pfa is an array of pointers to function returning T,即:pfa是个存放指向返回T类对象函数的指针的数组。

换种方式看,在这个例子中,pfa是名字,T(*[])()是类型。将(*pfa[])视为一体(direct-dcl),称为D1,那么可以写成T D1(),function returning object of T。在D1中,将*pfa视为一体(dcl),称为D2,那么*pfa[]应该是D2[](direct-dcl),array of D2。合起来就是array of D2 function returning object of T。D2是*pfa(dcl),替换到前面这句话,结果就是array of pointers to function returning object of T。

有了这些说明,可以试着做一下下面的题,看看自己是否真的理解了:

char **argv
argv: pointer to pointer to char
int (*daytab)[13]
daytab: pointer to array[13] of int
int *daytab[13]
daytab: array[13] of pointer to int
void *comp()
comp: function returning pointer to void
void (*comp)()
comp: pointer to function returning void
char (*(*x())[])()
x: function returning pointer to array[] of
pointer to function returning char
char (*(*x[3])())[5]
x: array[3] of pointer to function returning
pointer to array[5] of char
有了这个,就很容易理解下面这两个typedef:
typedef void (*disp)(int);
typedef void (*signal(int, disp))(int);

在C++中,规则比C要复杂一些。不过,基本思想保持不变,按照C的原则来理解复杂的声明,基本上就能满足要求了。没有在这里列出C++的规则一方面是因为太广,不能覆盖全;另一个原因就是,按照C的规则来就足够了,毕竟C++要与C兼容。

这里讨论的仅仅是声明,不涉及到类型的signature,因此相对来说还是比较简单的。

参考:
The C programming Language, by Brian W. Kernighan and Dennis M. Ritchie
The C++ programming Language, by Bjarne Stroustup
 
Copyleft (C) 2007-2009 raof01.
本文可以用于除商业外的所有用途。此处“用途”包括(但不限于)拷贝/翻译(部分或全部),不包括根据本文描述来产生代码及思想。若用于非商业,请保留此 权利声明,并标明文章原始地址和作者信息;若要用于商业,请与作者联系(raof01@gmail.com),否则作者将使用法律来保证权利。

我的更多文章
[发评论] 评论 重要提示:警惕虚假中奖信息!
  • chinaunix网友 2009-04-20 15:37
    mokaka好像搞错了吧
  • chinaunix网友 2009-03-15 12:03
    mokaka完全搞错了
  • fera 2009-03-15 11:17
    楼上的: 建议你去翻一下K&R看看你的答案队还是错。
  • chinaunix网友 2009-03-13 16:41
    声明 和 定义分开 声明 就是命名。 定义就是类型(* []都是告诉如何开辟内存的)。 非函数的小括号优先级最高 c里难懂的声明 都可以按 T D() 或 T D[] 依次分解 char (*(*x())[])() = T D() T = char,D=*(*x())[] 之后 D = T1 D1[] T1= void *,D1=*x(),之后D1 = T2 D2(),T2 = void*,D2=x, 所以最后答案是 声明的是个函数名x, x返回一个void*指针,而这指针又是指向[]个void*指针的指针(二重指针),而这个二重指针是返回为char的函数。
  • fera 2009-01-22 13:26
    对于T(&)[N]这样的类型,N是该类型不可缺少的部分。如果是T[N],那么N就不是类型的一部分,作为参数传递时需要额外指定N。
  • chinaunix网友 2008-02-27 11:35
    T (*)(const T &); T (&)(const T &); T [N]; T (&)[N]; T*; T&; ——实际上这些都是可用的类型,至少g++ 3.4.4能够通过编译,如: template void PassFuncPointer(T (*)(const T &)); 要注意其中*和&都用()括起来。
  • chinaunix网友 2007-10-16 13:34
    说一下关于模板的问题。C++中,模板并不是一个类型,而是类型的类型,也就是说,不是一个完整的类型。“template class AA;”中,AA是一个不完整类型,而AA才是整个类型。 另外,如果名字里面包含有很多其他的修饰,如域修饰、storage class、linkage,等等,这些都仅仅是修饰,不起实际作用,在理解时可以忽略。
  • chinaunix网友 2007-09-17 15:05
    收藏,顶
  • chinaunix网友 2007-09-13 10:49
    谢谢楼主的分享,我觉得很有用的,收藏了
  • chinaunix网友 2007-09-10 16:18
    C/C++代码怎么了?楼上的,说话注意点!!!
共 2 页
12
亲,您还没有登录,请[登录][注册]后再进行评论