一种高效解析函数指针声明语法的方法
A way to efficiently parse function pointer declaration syntax
所以,直到现在,我非常确定我的心理指针函数解析器能够解析甚至最难的指针……我错了!在阅读一些遗留代码时,我发现了这一点:
void (*(*somename)(void (*)()))(void (*)());
显然,意思是declare somename
as pointer to function (pointer to function returning void
) returning pointer to function (pointer to function returning void
)返回 void
(至少根据 http://cdecl.org)。
我似乎过于简化了函数指针声明的工作方式。我很确定语法是 return-type(*variable-name)(argument-types...)
。它适用于很多情况,但不适用于像上面这样的复杂情况。我怎样才能阅读这些不寻常和复杂的声明,而不必考虑所有语法规则并试图弄清楚我应该从左到右阅读还是反向阅读或以其他奇怪的方式阅读?
诀窍是使用顺时针螺旋规则http://c-faq.com/decl/spiral.anderson.html这里有点困难,因为括号太多了,但是一旦你弄明白了就没问题了。
此外,您还可以将复杂声明的一部分用标签做一个别名,当您理解其余部分时再返回标签。我的意思是:
void (*T)(void (*)());
你的 T 代替 (somename)(void ()())
C 语法允许无限嵌套各种事物,因此对于解析声明可能需要多少内存没有限制。解决这个问题:
- 在
void (*(*somename)(void (*)()))(void (*)())
中,我们看到有函数参数,所以我们把它们分开一点。
- 从右边开始,我们可以找到匹配到最右边的括号并插入空格以进行可视化:
void (*(*somename)(void (*)())) (void (*)())
.
- 所以我们看到这将
(*(*somename)(void (*)()))
声明为一个返回 void
并采用 void (*)()
类型参数的函数,该参数是指向 void
函数的指针没有原型。
- 接下来分析
(*(*somename)(void (*)()))
。左右括号匹配,所以这是 *(*somename)(void (*)())
.
- 这是指向前一个事物的指针(
void
函数采用指向 void
函数的指针,没有原型)。
- 如果剩下的足够简单,我们可能会看到它是一个指向没有原型的
void
函数的指针。
因此,somename
指向一个函数:
- 接受一个指向没有原型的
void
函数的指针,并且
- returns 指向
void
函数的指针采用指向 void
没有原型的函数的指针。
如果声明确实使您无法在没有帮助的情况下解析它,则可以构建一棵描述它的树。 C文法自然对应一棵树,学习文法与语法分析的相关理论和对应关系是计算机科学课程的一部分。正如问题所问,这对人类来说不是“有效的”,但它是分析声明的确定性方法。
我的一位教授教我们如何使用“right-left 规则”来做到这一点。他记录了这个 here.
以下是我将如何将其应用于此声明(从标识符向右移动开始)。
void (*(*somename)(void (*)()))(void (*)());
+-------^ somename
void (*(*somename)(void (*)()))(void (*)());
^--------+ is pointer
void (*(*somename)(void (*)()))(void (*)());
^---------+ (move left)
void (*(*somename)(void (*)()))(void (*)());
+----------^ to function
void (*(*somename)(void (*)()))(void (*)());
+----------------------^ taking (void (*)())
void (*(*somename)(void (*)()))(void (*)());
^-----------------------+ returning pointer
void (*(*somename)(void (*)()))(void (*)());
^------------------------+ (move left)
void (*(*somename)(void (*)()))(void (*)());
+-------------------------^ to function
void (*(*somename)(void (*)()))(void (*)());
+------------------------------------^ taking (void (*)())
void (*(*somename)(void (*)()))(void (*)());
^-----------------------------------------+ returning void
然后您可以将规则应用于参数列表中的每个参数,从括号中的任何内容开始,因为在这种情况下我们没有标识符:
void (*)()
+^ pointer
void (*)()
^-+ (move left)
void (*)()
+--^ to function
void (*)()
^--------+ returning void
我开发的方法是从最左边的标识符开始计算,记住以下优先规则:
T *a[N]; // a is an array of pointer
T (*a)[N]; // a is a pointer to an array
T *f(); // f is a function returning a pointer
T (*f)(); // if is a pointer to a function
并对任何函数参数递归执行此操作。
我将使用 λ 来表示未命名的参数,所以我们得到这样的结果:
somename -- somename is
*somename -- a pointer to
(*somename)( ) -- a function taking
(*somename)( λ ) -- unnamed parameter is
(*somename)( *λ ) -- a pointer to
(*somename)( (*λ)()) -- a function taking unspecified parameters
(*somename)(void (*λ)()) -- returning void
*(*somename)(void (*λ)()) -- returning a pointer to
(*(*somename)(void (*λ)()))( ) -- a function taking
(*(*somename)(void (*λ)()))( λ ) -- unnamed parameter is
(*(*somename)(void (*λ)()))( *λ ) -- a pointer to
(*(*somename)(void (*λ)()))( (*λ)()) -- a function taking unspecified parameters
(*(*somename)(void (*λ)()))(void (*λ)()) -- returning void
void (*(*somename)(void (*λ)()))(void (*λ)()); -- returning void
在英语中,somename
是指向一个函数的指针,该函数将指向另一个函数的指针作为参数,returns 是一个指针 另一个函数它以指向 另一个 函数的指针作为其参数,并且 returns void
.
这种令人讨厌的类型在野外很少见,但它们偶尔会出现。
所以,直到现在,我非常确定我的心理指针函数解析器能够解析甚至最难的指针……我错了!在阅读一些遗留代码时,我发现了这一点:
void (*(*somename)(void (*)()))(void (*)());
显然,意思是declare somename
as pointer to function (pointer to function returning void
) returning pointer to function (pointer to function returning void
)返回 void
(至少根据 http://cdecl.org)。
我似乎过于简化了函数指针声明的工作方式。我很确定语法是 return-type(*variable-name)(argument-types...)
。它适用于很多情况,但不适用于像上面这样的复杂情况。我怎样才能阅读这些不寻常和复杂的声明,而不必考虑所有语法规则并试图弄清楚我应该从左到右阅读还是反向阅读或以其他奇怪的方式阅读?
诀窍是使用顺时针螺旋规则http://c-faq.com/decl/spiral.anderson.html这里有点困难,因为括号太多了,但是一旦你弄明白了就没问题了。
此外,您还可以将复杂声明的一部分用标签做一个别名,当您理解其余部分时再返回标签。我的意思是:
void (*T)(void (*)());
你的 T 代替 (somename)(void ()())
C 语法允许无限嵌套各种事物,因此对于解析声明可能需要多少内存没有限制。解决这个问题:
- 在
void (*(*somename)(void (*)()))(void (*)())
中,我们看到有函数参数,所以我们把它们分开一点。 - 从右边开始,我们可以找到匹配到最右边的括号并插入空格以进行可视化:
void (*(*somename)(void (*)())) (void (*)())
. - 所以我们看到这将
(*(*somename)(void (*)()))
声明为一个返回void
并采用void (*)()
类型参数的函数,该参数是指向void
函数的指针没有原型。 - 接下来分析
(*(*somename)(void (*)()))
。左右括号匹配,所以这是*(*somename)(void (*)())
. - 这是指向前一个事物的指针(
void
函数采用指向void
函数的指针,没有原型)。 - 如果剩下的足够简单,我们可能会看到它是一个指向没有原型的
void
函数的指针。
因此,somename
指向一个函数:
- 接受一个指向没有原型的
void
函数的指针,并且 - returns 指向
void
函数的指针采用指向void
没有原型的函数的指针。
如果声明确实使您无法在没有帮助的情况下解析它,则可以构建一棵描述它的树。 C文法自然对应一棵树,学习文法与语法分析的相关理论和对应关系是计算机科学课程的一部分。正如问题所问,这对人类来说不是“有效的”,但它是分析声明的确定性方法。
我的一位教授教我们如何使用“right-left 规则”来做到这一点。他记录了这个 here.
以下是我将如何将其应用于此声明(从标识符向右移动开始)。
void (*(*somename)(void (*)()))(void (*)());
+-------^ somename
void (*(*somename)(void (*)()))(void (*)());
^--------+ is pointer
void (*(*somename)(void (*)()))(void (*)());
^---------+ (move left)
void (*(*somename)(void (*)()))(void (*)());
+----------^ to function
void (*(*somename)(void (*)()))(void (*)());
+----------------------^ taking (void (*)())
void (*(*somename)(void (*)()))(void (*)());
^-----------------------+ returning pointer
void (*(*somename)(void (*)()))(void (*)());
^------------------------+ (move left)
void (*(*somename)(void (*)()))(void (*)());
+-------------------------^ to function
void (*(*somename)(void (*)()))(void (*)());
+------------------------------------^ taking (void (*)())
void (*(*somename)(void (*)()))(void (*)());
^-----------------------------------------+ returning void
然后您可以将规则应用于参数列表中的每个参数,从括号中的任何内容开始,因为在这种情况下我们没有标识符:
void (*)()
+^ pointer
void (*)()
^-+ (move left)
void (*)()
+--^ to function
void (*)()
^--------+ returning void
我开发的方法是从最左边的标识符开始计算,记住以下优先规则:
T *a[N]; // a is an array of pointer
T (*a)[N]; // a is a pointer to an array
T *f(); // f is a function returning a pointer
T (*f)(); // if is a pointer to a function
并对任何函数参数递归执行此操作。
我将使用 λ 来表示未命名的参数,所以我们得到这样的结果:
somename -- somename is
*somename -- a pointer to
(*somename)( ) -- a function taking
(*somename)( λ ) -- unnamed parameter is
(*somename)( *λ ) -- a pointer to
(*somename)( (*λ)()) -- a function taking unspecified parameters
(*somename)(void (*λ)()) -- returning void
*(*somename)(void (*λ)()) -- returning a pointer to
(*(*somename)(void (*λ)()))( ) -- a function taking
(*(*somename)(void (*λ)()))( λ ) -- unnamed parameter is
(*(*somename)(void (*λ)()))( *λ ) -- a pointer to
(*(*somename)(void (*λ)()))( (*λ)()) -- a function taking unspecified parameters
(*(*somename)(void (*λ)()))(void (*λ)()) -- returning void
void (*(*somename)(void (*λ)()))(void (*λ)()); -- returning void
在英语中,somename
是指向一个函数的指针,该函数将指向另一个函数的指针作为参数,returns 是一个指针 另一个函数它以指向 另一个 函数的指针作为其参数,并且 returns void
.
这种令人讨厌的类型在野外很少见,但它们偶尔会出现。