关于声明的螺旋规律 - 当它是错误的?(The spiral rule about declarat

2019-09-01 07:38发布

笔者近日了解到螺旋规则为deobfuscating复杂的声明,必须已经写了一系列的类型定义的。 但是,下面的评论我报警:

一个经常被引用的简化,只适用于一些简单的情况。

我不觉得void (*signal(int, void (*fp)(int)))(int); 一个“简单的例子”。 这是所有的更令人震惊的,顺便说一句。

所以,我的问题是, 在这种情况下,我会正确应用规则,以及它会是错误的?

Answer 1:

从根本上说,规则根本不起作用,否则它的工作方式重新定义什么是螺旋的意思(在这种情况下,有一个在它没有意义考虑,例如:

int* a[10][15];

螺旋规则将给出是指针的阵列[10]至INT的阵列[15],这是错误的。 它的情况下,你的网站,它不工作,要么; 事实上,在案件signal ,它甚至没有明确的,你应该开始螺旋。

在一般情况下,它更容易找到的地方比实例,其中它的工作规则失败的例子。

我常想说,解析C ++的声明很简单,但没有身体谁已与复杂的声明试图会相信我。 在另一方面,它并不难,因为它有时做出来的人。 秘诀就是完全按照自己的表达式想到声明的,但少了很多运营商,和一个非常简单的优先规则:所有运营商的权利拥有对所有运营商的左优先。 在没有括号的,这意味着过程一切正确的第一,那么一切都在左边,和括号的过程完全按照您在任何其它的表达。 实际的困难不是语法本身,而是它的结果是一些非常复杂和违反直觉的声明,特别是在函数返回值和指向函数的参与:第一权,然后离开规则是指在一个特定的水平运营商常广泛分离,如:

int (*f( /* lots of parameters */ ))[10];

在这里展开最后期限int[10]但把[10]后的完整功能规范(至少对我来说)非常不自然,我不得不停止,每次做出来。 (这也可能是这个趋势在逻辑上相邻零件散开铅以螺旋的规则。但问题是,当然,在没有括号的,他们并不总是摊开,任何时候你看到[i][j] ,规则是向右走,然后向右又来了,而不是螺旋)。

而且,由于我们现在的表现来思考声明:当表达式变得太复杂,看你怎么办? 你为了使它更易于阅读引入中间变量。 在声明的情况下,“中间变量”是typedef 。 特别是,我认为,返回类型中的任何时间部分的函数参数(和很多其他时间也一样)结束后,你应该使用typedef使宣言更加简单。 (这是一个“听我的话,还不如我做”的规则,但是,我怕我会偶尔使用一些非常复杂的声明。)



Answer 2:

规则是正确的。 然而,人们应该在应用它非常小心。

我建议在C99 +的声明更正式的方式来使用它。

这里最重要的是认识到所有声明(以下递归结构constvolatilestaticexterninlinestructuniontypedef是从画面的简单删除,但可以很容易地添加回):

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 (指针)] base-type 1。

如果有递归,你会发现你的object在递归堆栈的底部(如果有任何),这将是最内层的一个,你会通过返回,并收集和衍生零件在各组合得到完整的声明递归的水平。

在解析你可以移动[object]到后[derived-part2] (如果有的话)。 这会给你一个线性,易于理解,声明(见上文1)。

因此,在

char* (**(*foo[3][5])(void))[7][9];

你得到:

  1. base-type = char
  2. 1级: derived-part1 = *object = (**(*foo[3][5])(void)) derived-part2 = [7][9]
  3. 2级: derived-part1 = **object = (*foo[3][5]) derived-part2 = (void)
  4. 3级: derived-part1 = *object = fooderived-part2 = [3][5]

从那里:

  1. 3级: * [3][5] foo
  2. 2级: ** (void) * [3][5] foo
  3. 1级: * [7][9] ** (void) * [3][5] foo
  4. 最后, char * [7][9] ** (void) * [3][5] foo

现在,读从右到左:

foo是5个指针3个阵列的返回指针的指针的指针9 7个数组的数组为char数组的函数(不采取PARAMS)。

你可以扭转每数组维度derived-part2的过程中也是如此。

这就是你的螺旋规则。

它很容易地看到螺旋。 你潜入越来越深嵌套[object]从左边,然后复出的唯一正确需要注意的是在上层还有另一对左,右等。



Answer 3:

例如:

int * a[][5];

这不是指针数组以阵列int



Answer 4:

螺旋规则实际上是看它的过于复杂的方式。 实际的规则要简单得多:

postfix is higher precedence than prefix.

而已。 这就是你需要记住。 在“复杂”的情况是,当你有括号覆盖该后缀,高比前缀的优先级,但你真的只需要找到匹配的括号,然后看括号第一里面的东西,而且,如果不彻底在parenthses外的一个新的水平拉,postfix的第一。

所以看你的复杂的例子

void (*signal(int, void (*fp)(int)))(int);

我们可以在任何名字开始,并找出这个名字是什么。 如果你开始在int ,大功告成- int是一个类型,你可以通过自己的理解。

如果你开始在fp ,FP不是一个类型,它的名字被宣布为东西。 所以看第一组括号包围的:

                        (*fp)

有没有后缀(处理后缀第一),然后前缀*意味着指针。 指针是什么? 尚未完成这么看出来另一个层面

                   void (*fp)(int)

后缀首先是“功能以一个int参数”,那么前缀是“返回void”。 因此,我们fn是“函数指针回吐INT PARAM,返回void”

如果我们启动一个signal ,第一级有一个后缀(功能)和一个前缀(返回指针)。 需要一个新的水平了,看看有什么指向(函数返回void)。 因此,我们结束了“有两个参数(int和函数指针)函数,返回指针一(INT)参数的功能,返回void”



文章来源: The spiral rule about declarations — when is it in error?
标签: c declaration