标题:[翻译] 如何阅读复杂的C定义/声明 出处:Felix021 时间:Sun, 08 Apr 2012 19:32:15 +0000 作者:felix021 地址:https://www.felix021.com/blog/read.php?2072 内容: 翻译自:How To Read C Declarations 英文原文 p.s. 以前还真没注意到这篇文章最后提到的vtable是啥意思…… 就算是非常有经验的C程序员,也对那些比简单数组/指针更复杂一些的声明感到头疼。比如说,下面这个是一个指针的数组,还是一个数组的指针?int *a[10]; 下面这货到底是什么?int (*(*vtable)[])(); 当然了,这货是一个指针,指向一个数组,这个数组的每个元素是一个指针,指向一个函数,函数的返回值类型是int :) 这篇短文希望能够教会你一个非常简单地读懂复杂声明的方法。我99%肯定我在80年代读过这篇,但是不记得具体是在什么地方读到的了。我怀疑是我自己发现这个的(尽管我总会被计算机语言结构和神秘的事物搞得很兴奋)。然而我的确记得,能够写出一个程序,将任何声明转换成英语。 == 黄金法则 == 这个法则是这样说的:引用 从标识符开始(或者最内层的结构,如果不存在标识符的话,通常出现于函数指针),首先向右看,直到遇到 ) 括号或者结束,看到什么就说出来;然后向左看,直到遇到 ( 括号或者回到行首,看到什么就说出来。跳出一层括号,重复上述过程:右看看,说出来;左看看,说出来。直到你说出变量的类型或者返回值(针对函数指针),也就表示你把声明都读完了。 最简单的情况是这样的:int i; 从 i 开始,你向右看,啥都没看到;然后就向左看,看到了int,说出来:i是一个int。 然后看个复杂一点的:int *a[3]; 从 a 开始:向右看,说“是一个包含3个元素的数组”;向左看,说“数组的每个元素是指针”;向右看,啥都没;向左看,说“指针指向int”。综合起来就是: a 是一个包含3个元素的数组,每个元素是一个指针,指向int。 加上一对括号让它看起来更怪异点儿:int (*a)[3]; 像在普通表达式中一样,括号改变了阅读/计算的顺序。从 a 开始:向右看,遇到括号了,往回;向左看,说“是一个指针”,遇到(括号,跳出来;向右看,[3],说“指向一个包含3个元素的数组”;向左看,int,说“数组的每个元素是int”。综合起来:a是一个指针,指向一个包含3个元素的数组,数组的每个元素是一个int。 好,再来看看这个:extern int *foo(); 赞,你说:foo是一个函数,返回一个指针,指向int。 接下来跳一步:就像我们可以定义一个指向int的指针,我们也可以定义一个指向函数的指针。在这种情况下,不需要extern了(因为不是函数的前向引用声明),而是一个变量的定义。这是一个基本的函数指针:int (*foo)(); 从foo开始:向右看,遇到括号,往回;向左看,*,说“是一个指针”,遇到左括号,跳出来;向右看,(),说“指向一个函数”;向左看,int,说“函数返回int”。综合起来:foo是一个指针,指向一个函数,函数返回int。 下面是一个数组,每个元素是一个指针,指向函数,函数返回int:int (*Object_vtable[])(); 你还需要最后一个,诡异的难以置信的声明:int (*(*vtable)[])(); 这是一个指针,指向一个数组,数组的每个元素是个指针,指向一个函数,函数的返回值是int。发现了吗?这货就是上面那个object_vtable的指针,也就是你定义的每一个对象需要的虚函数表(vtable)的指针。 这个指向vtable的指针是一个vtable的地址,例如,&Truck_vtable (就是某个Truck类的实例虚函数表的指针)。 == 总结 == 接下来的例子总结了所有C++为了实现多态性所建造的虚函数表需要的所有情形(就像最初的C Front - C++转C翻译器)。int *ptr_to_int; int *func_returning_ptr_to_int(); int (*ptr_to_func_returning_int)(); int (*array_of_ptr_to_func_returning_int[])(); int (*(*ptr_to_an_array_of_ptr_to_func_returning_int)[])(); Generated by Bo-blog 2.1.0