关于声明的螺旋规则 – 什么时候出错?
最近我学习了用于反混淆复杂声明的螺旋规则 , 该规则必须是用一系列typedef写的。 但是,下面的评论警告我:
一个经常被引用的简化,只适用于几个简单的情况。
我没有findvoid (*signal(int, void (*fp)(int)))(int); 一个“简单案例”。 顺便说一句,更令人担忧的是 
所以,我的问题是, 在哪种情况下我会正确适用这个规则,哪一个会出错呢?
从根本上说,这个规则根本不起作用,或者它是通过重新定义螺旋的含义来进行工作的(在这种情况下,没有任何意义,例如:
 int* a[10][15]; 
 螺旋规则会给出一个int指针数组[10]的数组[10],这是错误的。 它是你的网站的情况下,它也不工作; 事实上,在signal的情况下,甚至不清楚你应该在哪里开始螺旋。 
一般来说,find规则失败的例子比find它的例子要容易得多。
我经常试图说parsing一个C ++声明很简单,但没有谁试过复杂的声明的身体会相信我。 另一方面,它并不像有时候那样艰难。 其秘诀在于将声明完全按照您的expression方式来思考,但运算符less得多,而且优先级规则非常简单:右侧的所有运算符优先于左侧的所有运算符。 在没有括号的情况下,这意味着首先处理所有事情,然后处理所有事情,然后按照任何其他expression式处理括号。 实际的困难不是语法本身,而是它的结果是一些非常复杂和违反直觉的声明,特别是涉及函数返回值和指向函数的指针:第一个右边,然后是左边的规则,意味着特定级别的操作符是经常分开,例如:
 int (*f( /* lots of parameters */ ))[10]; 
 在这里扩展的最后一个术语是int[10] ,但是在完整的函数说明之后放置[10]是非常不自然的,我必须每次停下来解决这个问题。  (这可能是逻辑上相邻部分分散的趋势导致了螺旋规则,问题当然是在没有括号的情况下,它们并不总是展开 – 只要你看到[i][j] ,规则是正确的,然后再去正确的,而不是螺旋。) 
 因为我们现在正在考虑用expression式来expression声明:当expression式变得太复杂时,你会怎么做? 您可以引入中间variables以便于阅读。 在声明的情况下,“中间variables”是typedef 。 特别是,我会争辩说,任何时候返回types的一部分在函数参数(以及许多其他时间)之后结束,您应该使用typedef来使声明更简单。  (但是,这是一个“按照我所说的做,而不是按我所做”的规则,恐怕我会偶尔使用一些非常复杂的声明。 
规则是正确的。 但是,应用时要非常小心。
我build议以更正式的方式应用C99 +声明。
 这里最重要的是识别所有声明的下面的recursion结构( const , volatile , static , extern , inline , struct , union , typedef为简单起见从图片中删除,但可以轻松地添加回去): 
 base-type [derived-part1: *'s] [object] [derived-part2: []'s or ()] 
是的,就是这样,四个部分。
 where base-type is one of the following (I'm using a bit compressed notation): void [signed/unsigned] char [signed/unsigned] short [int] signed/unsigned [int] [signed/unsigned] long [long] [int] float [long] double etc object is an identifier OR ([derived-part1: *'s] [object] [derived-part2: []'s or ()]) * is *, denotes a reference/pointer and can be repeated [] in derived-part2 denotes bracketed array dimensions and can be repeated () in derived-part2 denotes parenthesized function parameters delimited with ,'s [] elsewhere denotes an optional part () elsewhere denotes parentheses 
一旦你已经分析了所有4个部分,
  [ object ]是[ derived-part2 ( derived-part2 / returning)] [ derived-part2 (pointer to)] base-type 1 。 
 如果有recursion,你会发现你的object (如果有的话)在recursion堆栈的底部,它将是最内层的一个,你将得到完整的声明,通过恢复和收集每个派生部分recursion水平。 
 parsing时,可以将[object]移动到[derived-part2] (如果有的话)。 这会给你一个线性的,易于理解的声明(见上面1 )。 
因此,在
 char* (**(*foo[3][5])(void))[7][9]; 
你得到:
-   base-type=char
-  等级1: derived-part1=*,object=(**(*foo[3][5])(void)),derived-part2=[7][9]
-  等级2: derived-part1=**,object=(*foo[3][5]),derived-part2=(void)
-  等级3: derived-part1=*,object=foo,derived-part2=[3][5]
从那里:
-  等级3: *[3][5]foo
-  等级2: **(void)*[3][5]foo
-  等级1: *[7][9]**(void)*[3][5]foo
-  最后, char*[7][9]**(void)*[3][5]foo
现在,从右向左阅读:
  foo是一个由3个数组组成的5个指针的数组(不带参数),它返回一个指针,指针指向一个由9个指向char的数组组成的数组。 
 您也可以在每个derived-part2中反转数组维度。 
这是你的螺旋规则。
 很容易看到螺旋。 你从左边潜入更深的嵌套[object] ,然后在右边重新performance,只有在上层有另一对左右等等。 
例如:
 int * a[][5]; 
 这不是指向int数组的指针数组。