简体   繁体   English

这段代码的含义是什么? void(* signal(int sig,void(* func)(int)))(int);

[英]What's the meaning of this piece of code? void (*signal(int sig, void (*func)(int)))(int);

I came across this piece of code and completely got lost interpreting its meaning. 我遇到了这段代码,完全迷失了解释它的含义。

#include <signal.h>
void (*signal(int sig, void (*func)(int)))(int);

What is a detailed explanation for the code at line 2? 第2行代码的详细解释是什么?

I know that void and int are types, the *func is a pointer for a function, and the brackets are for priority. 我知道voidint是类型,* func是函数的指针,括号是优先级。 But I still don't get the (*signal ...), the (int), and the whole thing combined together. 但是我仍然没有把(*信号......),(int)和整个事物结合在一起。 The more detailed, the better. 越详细越好。

Probably I've known the meaning/effect of this declaration. 可能我已经知道这个宣言的意义/效果了。 But I had to make some more trials to help me understand what's going on, as below: 但我不得不做一些试验来帮助我了解正在发生的事情,如下所示:

  1 #include <signal.h>
  2 void (*signal)(int sig, void (*func)(int));
  3 void (*signal)(int);  // then void (signal)(int) again.
  4 //void (*signal(int sig, void (*func)(int)))(int); //break this line into two lines above
  5
  6 int main(){}

In the above code, I broke void (*signal(int sig, void (*func)(int)))(int) into two lines. 在上面的代码中,我将void (*signal(int sig, void (*func)(int)))(int)分成两行。 For line 3, I tried both void (*signal)(int) and void (signal)(int) , with the same error result that indicated that I was trying to redeclare signal : 对于第3行,我尝试了void (*signal)(int)void (signal)(int) ,同样的错误结果表明我试图重新声明signal

TestDeclaration.c:2: error: 'signal' redeclared as different kind of symbol /usr/include/signal.h:93: error: previous declaration of 'signal' was here TestDeclaration.c:2:错误:'信号'重新声明为不同类型的符号/usr/include/signal.h:93:错误:之前'信号'的声明在这里
TestDeclaration.c:3: error: 'signal' redeclared as different kind of symbol /usr/include/signal.h:93: error: previous declaration of 'signal' was here TestDeclaration.c:3:错误:'signal'重新声明为不同类型的符号/usr/include/signal.h:93:错误:之前'signal'的声明在这里

Now I know both the trials are incorrect ways of declaration, but why are they incorrect? 现在我知道两种审判都是不正确的申报方式,但为什么它们不正确呢? Why is the original way of declaration NOT a redeclaration? 为什么声明的原始方式不是重新声明?

It's the declaration of a function taking an int and a pointer to a function (taking int returning void) and returning a pointer to a function (taking int and returning void). 它是一个函数的声明,它接受一个int和一个指向函数的指针(使int返回void)并返回一个指向函数的指针(取int并返回void)。


Explanation, or guide to interpretation 解释或解释指南

You can interpret by treating everything in parentheses as a single entity and then working inwards using the "declaration follows usage" rule. 您可以通过将括号中的所有内容视为单个实体进行解释,然后使用“声明跟随使用”规则向内工作。

void (*signal(int sig, void (*func)(int))) (int); void (* signal(int sig,void(* func)(int))) (int);

The entity in the brackets looks like a function taking int and returning void . 括号中的实体看起来像一个采用int并返回void的函数。

Stripping away the outer part: 剥去外部部分:

*signal(int sig, void (*func)(int))

So, signal takes some parameters and returns something that can be dereferenced (due to the leading * ) to form a function taking int and returning void . 因此, signal接受一些参数并返回可以被解除引用的东西(由于前导* )以形成一个接受int并返回void的函数。

This means signal is a function returning a pointer to a function (taking int and returning void ). 这意味着signal是一个返回指向函数的指针的函数(取int并返回void )。

Looking at the parameters it takes an int (ie sig ) and void (*func)(int) which is a pointer to a function (taking int and returning void ). 查看参数,它采用int (即sig )和void (*func)(int) ,它是一个指向函数的指针(取int并返回void )。

This is one of the classical examples of how convoluted C declarations can become. 这是复杂的C声明如何变成的经典例子之一。
To understand this declaration, it usually helps to introduce a typedef: 要理解此声明,通常有助于引入typedef:

typedef void (*sighandler_t)(int);
sighandler_t signal(int sig, sighandler_t func);

The typedef declares a pointer to a function (taking an int parameter and returning nothing). typedef声明一个指向函数的指针(接受一个int参数并且不返回任何内容)。 The function signal can now be seen as a function that takes two parameters (an int and a pointer to a function) and returns a pointer to a function. 函数signal现在可以看作是一个函数,它接受两个参数(一个int和一个指向函数的指针)并返回一个指向函数的指针。

This can also be derived from the original declaration, but it takes a bit of practice. 这也可以从原始声明中获得,但需要一些练习。 The usual way is to start at the identifier that names the outermost entity ( signal is this case): 通常的方法是从标识最外层实体的标识符开始( signal是这种情况):

signal is a ... signal是......

Then you read right until you find an unmatched closing parenthesis or the end of the declaration: void (* signal (int sig, void (*func)(int) )(int) 然后你向右读,直到你找到一个不匹配的右括号或声明的结尾: void (* signal (int sig, void (*func)(int) )(int)

signal is a function taking ... returning ... signal是一个功能......返回...

Now you can choose between parsing the parameters first, or the return value first. 现在,您可以选择首先解析参数,还是首先解析返回值。 I will do the return value first. 我会先做返回值。 For that, you read backwards to find the matching open parenthesis: void ( signal( / ... */ ) ) (int) 为此,您向后读取以找到匹配的左括号: void ( signal( / ... */ ) ) (int) void ( signal( / ... */ ) ) (int)

`signal is a function taking ... returning a pointer to ... `signal是一个函数......返回指针...

Reading back and forth this way you get at successive stages: 以这种方式来回读取你会在连续的阶段得到:

`signal is a function taking ... returning a pointer to a (function taking ... returning ...) `signal是一个函数...返回一个指向a的函数(函数正在......返回......)

`signal is a function taking ... returning a pointer to a (function taking ... returning void) `signal是一个函数...返回一个指针(函数取...返回void)

`signal is a function taking ... returning a pointer to a (function taking an int and returning void) `signal是一个函数...返回一个指向(函数接受int并返回void)的指针

`signal is a function taking two parameters: (an int) and (a pointer to a function taking an int and returning void), and returning a pointer to a (function taking an int and returning void) `signal是一个带有两个参数的函数:(一个int)和一个指向函数的指针,它接受一个int并返回void,然后返回一个指针(一个带有int并返回void的函数)

A mnemonic I created many years ago, which is invaluable when trying to understand complicated types: 我多年前创建的助记符,在尝试理解复杂类型时非常有用:

Remember these rules for C declares
And precedence never will be in doubt
Start with the Suffix, Proceed with the Prefix
And read both sets from the inside, out.

Except where parentheses change that precedence, of course. 当然,除非括号改变了优先顺序。

Applying it to this case: 将它应用于这种情况:

void (*signal(int sig, void (*func)(int)))(int);

signal is:
  [inside parentheses]
  [suffix ()] a function, whose arguments are
    sig, which is [prefix int] an integer, and
      func, which is:
         [inside parentheses]
           [no suffix within these parens]
           [prefix *] a pointer to
         [suffix ()] a function, whose argument is
           an int
         [no more suffixes]
         [prefix void] and which returns void
         [no more prefixes]
       [no more arguments]
     [prefix *] And which returns a pointer to
     [no more prefixes within these parens]
   [suffix ()] a function, whose argument is
      an int
   [no more suffixes]
   [prefix void] and which returns void.

With a bit of practice, you'll get to the point where you can do all that on the fly: 通过一些练习,您将达到可以即时完成所有操作的程度:

"Signal is function, whose arguments are:
    sig, an integer,
    and func, a pointer to a function whose argument is an int and which returns void
... which returns a pointer to a function that takes int as an argument and returns void.

(Sorry about the error first time out -- I'm out of practice.) (抱歉第一次出错 - 我没有练习。)

Yes, that mnemonic (with the implied "except for parentheses, of course) works for all C declarations, no matter how badly the pointers, arrays, and functions are intermixed. 是的,无论指针,数组和函数混合多么糟糕,那个助记符(当然除了括号外)都适用于所有C声明。

This is a REALLY useful skill to have when trying to figure out how someone else's code works... or even figuring out something of your own that you haven't seen in a long time. 在尝试弄清楚别人的代码是如何工作的时候,这是一项真正有用的技巧......甚至可以找出你很久没见过的自己的东西。

But, yes, the better way to handle anything that you don't think people will be able to read at a glance is to build it up in layers with typedefs. 但是,是的,处理你认为人们无法一目了然的任何事情的更好方法是使用typedef在层中构建它。 The component types are likely to be useful themselves, and taking it one step at a time keeps people from getting lost trying to figure out which parenthesis matches which. 组件类型本身可能很有用,并且一次采取一个步骤可以防止人们迷失,试图弄清楚哪个括号匹配哪个。 Be kind to the next person who touches your code! 善待接触你代码的下一个人!

If you find the mnemonic useful, feel free to quote it elsewhere -- just give me credit as its author, please. 如果您发现助记符很有用,请随意在其他地方引用它 - 请给我作为作者的信誉。

By the way, there are also "C Explainer" tools which will parse C delaractions and do the conversion to English description for you. 顺便说一句,还有“C解释器”工具,它将解析C解体并为您转换为英文描述。 Mine was called CEX, for obvious reasons, but many others exist and you should be able to find one if you don't want to commit this skill to wetware or if someone hands you something that's really too ugly for you to keep track of. Mine被称为CEX,原因显而易见,但是还有很多其他的存在,如果你不想将这种技能用于湿软件,或者如果有人向你提供的东西真的太难以让你跟踪,那么你应该找到一个。

Let's take an example of how this nasty declaration could be used: 让我们举一个如何使用这个令人讨厌的声明的例子:

void (*signal(int sig, void (*func)(int)))(int);

Without too much verbosity, we could say that "signal" is a function with two parameters that returns a function. 没有太多冗长,我们可以说“signal”是一个带有两个返回函数的参数的函数。

#include <stdio.h>

// First function that could be returned by our principal function
// Note that we can point to it using void (*f)(int)
void car_is_red(int color)
{
    printf("[car_is_red] Color %d (red) is my favorite color too !\n", color);
}

// Second function that could be returned by our principal function
// Note that we can point to it using void (*f)(int)
void car_is_gray(int color)
{
    printf("[car_is_gray] I don't like the color %d (gray) either !\n", color);
}

// The function taken as second parameter by our principal function
// Note that we can point to it using void (*func)(int)
void show_car_type(int mod)
{
    printf("[show_car_type] Our car has the type: %d\n",mod);
}

/* Our principal function. Takes two parameters, returns a function. */
void (* show_car_attributes(int color, void (*func)(int)) )(int)
{
    printf("[show_car_attributes] Our car has the color: %d\n",color); // Use the first parameter

    int mod = 11;  // Some local variable of our function show_car_attributes()
    func(mod);  // Call the function pointed by the second parameter (i.e. show_car_type() )

    // Depending on color value, return the pointer to one of two functions
    // Note that we do NOT use braces with function names
    if (color == 1)
        return car_is_red;
    else
        return car_is_gray;
    }


//main() function
int main()
{
    int color = 2;   // Declare our color for the car
    void (*f)(int);  // Declare a pointer to a function with one parameter (int)

    f = show_car_attributes(color, show_car_type); // f will take the return 
           // value of our principal function. Stated without braces, the 
           // parameter  "show_car_types" is a function pointer of type 
           // void (*func)(int).

    f(color);  // Call function that was returned by show_car_attributes()

    return 0;
}

Let's see what will be output: 让我们看看将会输出什么:

If color = 1 如果color = 1

[show_car_attributes] Our car has the color: 1
[show_car_type] Our car has the type: 11
[car_is_red] Color 1 (red) is my favorite color too !

If color = 2 如果color = 2

[show_car_attributes] Our car has the color: 2
[show_car_type] Our car has the type: 11
[car_is_gray] I don't like the color 2 (gray) either !

Returning pointer to a function which takes an: 返回指向一个函数的指针:

  • integer as first argument argument and 整数作为第一个参数参数和
  • a pointer to a function (which takes an int and returns void) as an argument as a second argument. 指向函数的指针(接受int并返回void)作为参数作为第二个参数。

And takes an integer argument. 并采用整数参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM