笔者近日了解到螺旋规则为deobfuscating复杂的声明,必须已经写了一系列的类型定义的。 但是,下面的评论我报警:
一个经常被引用的简化,只适用于一些简单的情况。
我不觉得void (*signal(int, void (*fp)(int)))(int);
一个“简单的例子”。 这是所有的更令人震惊的,顺便说一句。
所以,我的问题是, 在这种情况下,我会正确应用规则,以及它会是错误的?
笔者近日了解到螺旋规则为deobfuscating复杂的声明,必须已经写了一系列的类型定义的。 但是,下面的评论我报警:
一个经常被引用的简化,只适用于一些简单的情况。
我不觉得void (*signal(int, void (*fp)(int)))(int);
一个“简单的例子”。 这是所有的更令人震惊的,顺便说一句。
所以,我的问题是, 在这种情况下,我会正确应用规则,以及它会是错误的?
从根本上说,规则根本不起作用,否则它的工作方式重新定义什么是螺旋的意思(在这种情况下,有一个在它没有意义考虑,例如:
int* a[10][15];
螺旋规则将给出是指针的阵列[10]至INT的阵列[15],这是错误的。 它的情况下,你的网站,它不工作,要么; 事实上,在案件signal
,它甚至没有明确的,你应该开始螺旋。
在一般情况下,它更容易找到的地方比实例,其中它的工作规则失败的例子。
我常想说,解析C ++的声明很简单,但没有身体谁已与复杂的声明试图会相信我。 在另一方面,它并不难,因为它有时做出来的人。 秘诀就是完全按照自己的表达式想到声明的,但少了很多运营商,和一个非常简单的优先规则:所有运营商的权利拥有对所有运营商的左优先。 在没有括号的,这意味着过程一切正确的第一,那么一切都在左边,和括号的过程完全按照您在任何其它的表达。 实际的困难不是语法本身,而是它的结果是一些非常复杂和违反直觉的声明,特别是在函数返回值和指向函数的参与:第一权,然后离开规则是指在一个特定的水平运营商常广泛分离,如:
int (*f( /* lots of parameters */ ))[10];
在这里展开最后期限int[10]
但把[10]
后的完整功能规范(至少对我来说)非常不自然,我不得不停止,每次做出来。 (这也可能是这个趋势在逻辑上相邻零件散开铅以螺旋的规则。但问题是,当然,在没有括号的,他们并不总是摊开,任何时候你看到[i][j]
,规则是向右走,然后向右又来了,而不是螺旋)。
而且,由于我们现在的表现来思考声明:当表达式变得太复杂,看你怎么办? 你为了使它更易于阅读引入中间变量。 在声明的情况下,“中间变量”是typedef
。 特别是,我认为,返回类型中的任何时间部分的函数参数(和很多其他时间也一样)结束后,你应该使用typedef
使宣言更加简单。 (这是一个“听我的话,还不如我做”的规则,但是,我怕我会偶尔使用一些非常复杂的声明。)
规则是正确的。 然而,人们应该在应用它非常小心。
我建议在C99 +的声明更正式的方式来使用它。
这里最重要的是认识到所有声明(以下递归结构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
(指针)] base-type
1。
如果有递归,你会发现你的object
在递归堆栈的底部(如果有任何),这将是最内层的一个,你会通过返回,并收集和衍生零件在各组合得到完整的声明递归的水平。
在解析你可以移动[object]
到后[derived-part2]
(如果有的话)。 这会给你一个线性,易于理解,声明(见上文1)。
因此,在
char* (**(*foo[3][5])(void))[7][9];
你得到:
base-type
= char
derived-part1
= *
, object
= (**(*foo[3][5])(void))
derived-part2
= [7][9]
derived-part1
= **
, object
= (*foo[3][5])
derived-part2
= (void)
derived-part1
= *
, object
= foo
, derived-part2
= [3][5]
从那里:
*
[3][5]
foo
**
(void)
*
[3][5]
foo
*
[7][9]
**
(void)
*
[3][5]
foo
char
*
[7][9]
**
(void)
*
[3][5]
foo
现在,读从右到左:
foo
是5个指针3个阵列的返回指针的指针的指针9 7个数组的数组为char数组的函数(不采取PARAMS)。
你可以扭转每数组维度derived-part2
的过程中也是如此。
这就是你的螺旋规则。
它很容易地看到螺旋。 你潜入越来越深嵌套[object]
从左边,然后复出的唯一正确需要注意的是在上层还有另一对左,右等。
例如:
int * a[][5];
这不是指针数组以阵列int
。
螺旋规则实际上是看它的过于复杂的方式。 实际的规则要简单得多:
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”