这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » MCU » 再再论指针

共13条 2/2 1 2 跳转至
院士
2011-01-27 13:18:19     打赏
11楼
第十章 围绕p()与(*p)()的争论    对于一个函数:   void func(void);   我们通常可以定义一个这样的函数指针指向它:   void (*p)(void) = func;   通过p调用func时,通常有两种写法:   p();或者(*p)();   围绕这两种写法,当初C89制定的时候曾经有过争论。(*p)();是一种旧式的规定,旧式规定圆括号左边必须具有“函数”类型,如果是指向函数的指针,那么必须加上*声明符。但C89不再把圆括号的左边限定为“函数”类型,而是一个后缀表达式。那么问题就来了,如果p的值是函数地址,那么*号就是声明符,但如果p指向的内容是函数地址,*号就得被看作运算符了。同一种形式会有两种解释,这是一个矛盾。不仅函数调用如此,指向数组的指针也存在这种矛盾。编译器为了处理这种情况得增加代码,效率自然就降低了。争论的最后结果是谁也不能把对方完全说服,于是就干脆两种都支持了。笔者认为应该抛弃旧式的规定,p();这种形式简洁明了,又符合函数的一般形式,何乐而不为?         第八章练习的答案,同时给出用typedef的分解方法:     int (*(*func)[5][6])[7][8];   func是一个指向数组的指针,这类数组的元素是一个具有5X6个int元素的二维数组,而这个二维数组的元素又是一个二维数组。   typedef int (*PARA)[7][8]; typedef PARA (*func)[5][6];     int (*(*(*func)(int *))[5])(int *);   func是一个函数指针,这类函数的返回值是一个指向数组的指针,所指向数组的元素也是函数指针,指向的函数具有int*形参,返回值为int。   typedef int (*PARA1)(int*); typedef PARA1 (*PARA2)[5]; typedef PARA2 (*func)(int*);   int (*(*func[7][8][9])(int*))[5];   func是一个数组,这个数组的元素是函数指针,这类函数具有int*的形参,返回值是指向数组的指针,所指向的数组的元素是具有5个int元素的数组。   typedef int (*PARA1)[5]; typedef PARA1 (*PARA2)(int*); typedef PARA2 func[7][8][9];  

院士
2011-01-27 13:19:41     打赏
12楼

《再再论指针》后记
     在这篇后记中,笔者将对三个问题进行补充:

一、关于数组名取地址的问题。c89、c99允许对数组名取地址,是由于数组符合一个对象的定义,按照一个对象的语义,对其取地址是合理的。但矛盾在于,数组名是一个符号地址,是一个右值,对其取地址不符合&运算符的语法。c89、c99委员会经过权衡,认为维护一个对象的合理性比一个运算符更重要、更合
理,因此允许对数组名取地址。但是,&a的意义,已经不是对一个数组名取地址,而是对一个数组对象取

    地址,因此,&a所代表的地址值才跟a地址值一样,同时sizeof(&a)应该等于sizeof(a)。c89、c99对这个

     观点是这样阐述的:

     Some implementations have not allowed the & operator to be applied to an array or a function.(The construct was permitted in early versions of C, then later made optional.) The C89 Committee endorsed the construct since it is unambiguous, and since data abstraction is enhanced by allowing the important & operator to apply uniformly to any addressable entity.

二、对于二级const指针间的赋值,笔者曾经在第九章举了一个例子,以说明二级const指针间的赋值会导

     致一个常量被修改。这里再补充一条理由。以const int *p1和int *p2为例类型比较涉及两个方面。首先,p1和p2所指向的对象都是int,这是相容的;p1带有const而p2没有,这也是相容的,因为它符合“指针间赋值左值要包含右值所有的限定词”这条规则。但对于const int ** p1和int **p2来说,情况就不一样了。虽然p1仍然符合“左值包含右值所有限定词”规则,但p1所指向的对象是一个指向const对象的指针,而p2所指向的对象却是一个指向非const对象的指针,两者的类型不相容,因此p1=p2非法。这条理由是一种已过时的现象,更多地存在于早期的编译器里,由于它往往让用户难以理解,因此后期的编译器都倾向于允许这种赋值。

三、关于p()与(*p)()之间的争论,有人提出,由于c89存在一条隐含声明,会使得p()这种形式存在危险性。

    首先,c89、c99推荐使用p()这种形式已经是盖棺定论的事实,两个标准是这样说的:

    Pointers to functions may be used either as (*pf)() or as pf(). The latter construct, not sanctioned in K&R, appears in some present versions of C, is unambiguous, invalidates no old code, and can be an important shorthand. The shorthand is useful for packages that present  only one external name, which designates a structure full of pointers to objects and functions: member functions can be called as graphics.open(file) instead of (*graphics.open)(file).
...........................
    The C89 Committee saw no real harm in allowing these forms; outlawing forms like (*f)(), while still permitting *a for a[], simply seemed more trouble than it was worth.

可见(*p)()这种形式是K&R时代的旧式规定,c89、c99仍然支持它只不过是因为对旧代码的支持。

    第二,这些人以为,未声明p而使用p()的时候,由于这条隐含声明的存在,p就是已被声明了,这是对隐含声明的错误理解,实际上c89、c99只是把这种情况下的p()当作extern int p();而已,在语法上,p仍然是一个未声明的标识符!它违反了另一条规则:一个标识符应当在声明之后才能使用。但它不应该象其它未声明标识符那样产生一条error,否则隐含声明跟不存在没有什么两样。它应该产生一条warning,以说明p是undefined或者函数原型未声明。

    如果一定要说会产生什么问题的话,那就是可能会有某些程序员忽略这个warning,这会产生一些问题。

    但这是设计者的原因,不是标准的原因。出于杜绝这种隐患的原因,c99干脆废除了c89的隐含声明,以使

    编译器产生一条error。c99关于这一点是这样说的:

    The rule for implicit declaration of functions has been removed in C99.The effect is to guarantee the production of a diagnostic that will catch an additional category of programming errors.


第四。第十章笔者解释了函数的返回值不能为数组的原因,文中笔者是这样写的:

int func(void) [5];

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

以上这个解释有偏差,不应当用数组名来解释这个问题,因为数组名不是数组对象的引用,在C中,没有对数组对象的引用,因此,函数的返回值不能为数组。


专家
2011-01-27 14:35:57     打赏
13楼
好东西,不得不顶一个!

共13条 2/2 1 2 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]