当前位置:  开发笔记 > 编程语言 > 正文

C不是那么难:void(*(*f [])())()

如何解决《C不是那么难:void(*(*f[])())()》经验,为你挑选了9个好方法。

我今天刚看了一张照片,觉得我很感激解释.所以这是图片:

一些c代码

我发现这令人困惑,并想知道这些代码是否实用.我用Google搜索了这张照片,并在此 reddit条目中找到了另一张照片,这是照片:

一些有趣的解释

那么"螺旋式阅读"是有效的吗?这是C编译器解析的方式吗?
如果对这个奇怪的代码有更简单的解释,那就太好了.
除此之外,这些代码是否有用?如果是这样,何时何地?

有一个关于"螺旋规则"的问题,但我不只是询问它是如何应用的,或者是如何用该规则读取表达式的.我也质疑这种表达方式的使用和螺旋规则的有效性.关于这些,已经发布了一些很好的答案.



1> ouah..:

有一个称为"顺时针/螺旋规则"的规则来帮助找到复杂声明的含义.

来自c-faq:

有三个简单的步骤:

    从未知元素开始,以螺旋/顺时针方向移动; 在制定以下元素时,用相应的英语声明替换它们:

    [X][]
    =>数组X大小...或数组未定义大小...

    (type1, type2)
    =>函数传递type1和type2返回...

    *
    =>指针到......

    继续以螺旋/顺时针方向执行此操作,直到所有令牌都被覆盖.

    始终先解决括号中的任何内容!

您可以查看上面的链接以获取示例.

另请注意,为了帮助您,还有一个名为的网站:

http://www.cdecl.org

您可以输入C声明,它将赋予其英语含义.对于

void (*(*f[])())()

它输出:

声明f为指向函数的指针数组,返回指向函数返回void的指针

编辑:

正如Random832的评论中所指出的,螺旋规则不会解决数组数组,并且会导致(大多数)这些声明中的错误结果.例如,int **x[1][2];螺旋规则忽略了[]优先级高的事实*.

当在数组数组前面时,可以在应用螺旋规则之前首先添加显式括号.例如:由于优先级而与(也是有效的C)int **x[1][2];相同,int **(x[1][2]);然后螺旋规则正确地将其读作"x是指向int的指针的数组2的数组1",这是正确的英语声明.

请注意,这个问题也被包括在本答案由詹姆斯·甘孜(中指出haccks在评论).


没有"螺旋规则"......"int***foo [] [] []"定义指向指针指针的数组数组."螺旋"只是因为这个声明恰好将括号中的东西组合成一种导致它们交替的方式.在每组括号内,它是右边的一切,然后是左边的.
我希望cdecl.org更好
@vsoftco但是如果你到达圆括号时只是转身,那就不是"以螺旋/顺时针方向移动".

2> John Bode..:

"螺旋"规则类型不符合以下优先规则:

T *a[]    -- a is an array of pointer to T
T (*a)[]  -- a is a pointer to an array of T
T *f()    -- f is a function returning a pointer to T
T (*f)()  -- f is a pointer to a function returning T

下标[]和函数调用()操作符的优先级高于一元*,因此*f()被解析为*(f())*a[]解析为*(a[]).

因此,如果您需要指向数组的指针或指向函数的指针,那么您需要*使用标识符显式地对其进行分组,如(*a)[](*f)().

然后你意识到这一点,a并且f可以是比标识符更复杂的表达式; in T (*a)[N],a可以是一个简单的标识符,或者它可以是一个函数调用,如(*f())[N](a- > f()),或者它可以是一个数组,如(*p[M])[N](a- > p[M]),或者它可以是一个指向函数的指针数组,如(*(*p[M])())[N](a- > (*p[M])()),等等

如果间接运算符*是后缀而不是一元,这将是很好的,这将使声明从左到右更容易阅读(void f[]*()*();肯定流量更好void (*(*f[])())()),但事实并非如此.

当你遇到像这样的毛茸茸的声明时,首先找到最左边的标识符并应用上面的优先规则,递归地将它们应用于任何函数参数:

         f              -- f
         f[]            -- is an array
        *f[]            -- of pointers  ([] has higher precedence than *)
       (*f[])()         -- to functions
      *(*f[])()         -- returning pointers
     (*(*f[])())()      -- to functions
void (*(*f[])())();     -- returning void

signal标准库中的函数可能是这种疯狂的类型标本:

       signal                                       -- signal
       signal(                          )           -- is a function with parameters
       signal(    sig,                  )           --    sig
       signal(int sig,                  )           --    which is an int and
       signal(int sig,        func      )           --    func
       signal(int sig,       *func      )           --    which is a pointer
       signal(int sig,      (*func)(int))           --    to a function taking an int                                           
       signal(int sig, void (*func)(int))           --    returning void
      *signal(int sig, void (*func)(int))           -- returning a pointer
     (*signal(int sig, void (*func)(int)))(int)     -- to a function taking an int
void (*signal(int sig, void (*func)(int)))(int);    -- and returning void

此时大多数人都说"使用typedefs",这当然是一个选择:

typedef void outerfunc(void);
typedef outerfunc *innerfunc(void);

innerfunc *f[N];

但...

你会如何在表达中使用 f?你知道它是一个指针数组,但你如何使用它来执行正确的函数?你必须查看typedef并解开正确的语法.相比之下,"裸"版本非常引人注目,但它确切地告诉你如何在表达式中使用 f(即(*(*f[i])())();,假设两个函数都没有参数).


感谢您提供"信号"的示例,表明这些事情确实出现在野外.

3> Jon Purdy..:

在C中,声明镜像用法 - 它是如何在标准中定义的.声明:

void (*(*f[])())()

断言表达式(*(*f[i])())()产生类型的结果void.意思是:

f 必须是一个数组,因为你可以索引它:

f[i]

f必须是指针的元素,因为你可以取消引用它们:

*f[i]

那些指针必须是不带参数的函数的指针,因为你可以调用它们:

(*f[i])()

这些函数的结果也必须是指针,因为你可以取消引用它们:

*(*f[i])()

那些指针必须是不带参数的函数的指针,因为你可以调用它们:

(*(*f[i])())()

那些函数指针必须返回 void

"螺旋规则"只是一种助记符,提供了一种理解同一事物的不同方式.


尼斯.从这个角度来看,它真的很简单.实际上比`vector ()>*> f`更容易,特别是如果你添加`std ::`s.(但是,例子_is_设计......甚至`f :: [IORef(IO(IO()))]`看起来很奇怪.)
到目前为止,这是考虑C类型的最简单方法,多亏了这一点
我喜欢这个!比愚蠢的螺旋更容易推理.`(*f [])()`是一种你可以索引的类型,然后取消引用,然后调用,所以它是一个指向函数的指针数组.
看待它的好方法,我以前从未见过.+1

4> haccks..:

那么"螺旋式阅读"是有效的吗?

应用螺旋规则或使用cdecl始终无效.在某些情况下都失败了.螺旋规则适用于许多情况,但它并不普遍.

要破译复杂的声明,请记住这两个简单的规则:

始终从内到外阅读声明:从最里面的(如果有的话)括号开始.找到正在声明的标识符,并从那里开始解密声明.

当有选择时,总是喜欢[]并且()结束*:如果*在标识符之前并且在标识符[]之后,则标识符表示数组,而不是指针.同样,如果*在标识符之前并且在标识符()之后,则标识符表示函数,而不是指针.(括号可以始终用于覆盖普通优先级[]()以上*.)

该规则实际上涉及从标识符的一侧到另一侧的曲折.

现在破译一个简单的声明

int *a[10];

应用规则:

int *a[10];      "a is"  
     ^  

int *a[10];      "a is an array"  
      ^^^^ 

int *a[10];      "a is an array of pointers"
    ^

int *a[10];      "a is an array of pointers to `int`".  
^^^      

让我们破译复杂的声明

void ( *(*f[]) () ) ();  

通过适用上述规则:

void ( *(*f[]) () ) ();        "f is"  
          ^  

void ( *(*f[]) () ) ();        "f is an array"  
           ^^ 

void ( *(*f[]) () ) ();        "f is an array of pointers" 
         ^    

void ( *(*f[]) () ) ();        "f is an array of pointers to function"   
               ^^     

void ( *(*f[]) () ) ();        "f is an array of pointers to function returning pointer"
       ^   

void ( *(*f[]) () ) ();        "f is an array of pointers to function returning pointer to function" 
                    ^^    

void ( *(*f[]) () ) ();        "f is an array of pointers to function returning pointer to function returning `void`"  
^^^^

这是一个GIF演示你如何去(点击图片查看大图):

在此输入图像描述


这里提到的规则来自KN KING的C Programming A Modern Approach一书.



5> Random832..:

它只是一个"螺旋",因为在这个声明中,每个括号内的每一侧只有一个算子.声称你以"螺旋形式"进行通常会建议你在声明中的数组和指针之间交替,而int ***foo[][][]实际上所有的数组级别都在任何指针级别之前.



6> SergeyA..:

我怀疑像这样的结构在现实生活中会有任何用处.我甚至厌恶他们作为常规开发人员的面试问题(对于编译器编写者来说可能是好的).应该使用typedef.


尽管如此,知道如何解析它很重要,即使只知道如何解析typedef!
在面试中询问这些类型问题的人只会用它来抚摸他们的Egos.

7> 小智..:

作为一个随机的琐事,你可能会发现在英语中有一个实际的词来描述如何读取C声明是有趣的:Boustrophedonically,即从右到左,从左到右交替.

参考文献:范德林登,1994年 - 第76页



8> Casey..:

关于这个的用处,在使用shellcode时你会看到这个构造很多:

int (*ret)() = (int(*)())code;
ret();

虽然在语法上并不复杂,但这种特殊模式出现了很多.

在这个 SO问题中有更完整的例子.

因此,虽然原始图片中的有用性是有问题的(我建议任何生产代码应该大大简化),但是有一些语法结构确实出现了很多.



9> August Karls..:

声明

void (*(*f[])())()

只是一种晦涩难懂的说法

Function f[]

typedef void (*ResultFunction)();

typedef ResultFunction (*Function)();

实际上,需要更多描述性名称而不是ResultFunctionFunction.如果可能的话,我也会将参数列表指定为void.

推荐阅读
跟我搞对象吧
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有