0

I'm thinking about the difference between:

void *signal(int, void (*)(int))(int)

and

void (*signal(int, void (*)(int)))(int)

I know the latter is from here - Example #3: The ``Ultimate'' (it's a hilarious learning experience when I was trying speak out loud to understand it):

signal is a function takes (int, void (*)(int)) as input and returns a pointer to another function that takes (int) and returns void.

For the former I'm thinking that since the last (int) will have higher precedence than * so it should be a syntax error, but from cdecl.org the result is:

["] declare signal as function (int, pointer to function (int) returning void) returning function (int) returning pointer to void [."]

So I need a check.

Kindred
  • 1,229
  • 14
  • 41
  • What is the question? – Lundin Nov 20 '18 at 13:44
  • @Lundin: yes, my question is that I use a technique like *precedence order* to understand this kind of definition, in the former one I thought the last `(int)` will associate first before the first `*`, and this will cause a syntax error. You would see my comment on @Michael Kenzel's answer if you want to know. – Kindred Nov 20 '18 at 13:47
  • This has absolutely nothing to do with operator precedence. If you want some aid in how to think when reading obscure stuff, check this: http://c-faq.com/decl/spiral.anderson.html – Lundin Nov 20 '18 at 13:48
  • @Lundin: you're right but that's the link I provided in my question, I read those description but I got the link from someone in the chatroom and he said it's not a formal technique. – Kindred Nov 20 '18 at 13:50
  • @Lundin: ok, sorry for my mistake, I made the conclusion from an example like `int *a[2][2]` is first right-associative then dealing with remaining `int *` as element type in the array. Then I thought this would also work for parentheses `()` after the given identifier. – Kindred Nov 20 '18 at 13:53
  • I posted an answer trying to explain this without "spirals". – Lundin Nov 20 '18 at 14:29
  • @Lundin: Thank you sir, I read it now and appreciate your time and kindness. – Kindred Nov 20 '18 at 14:31

2 Answers2

6

One has to differentiate between grammar and semantics. cdecl.org only gives you the grammatical meaning of whatever declarator you type into it. In your first example, you have indeed a grammatically correct declaration of signal as a function returning a function. However, C does not allow functions to return other functions:

N1570 6.7.6.3 §1:

A function declarator shall not specify a return type that is a function type or an array type.

So while this declaration is grammatically correct, it is semantically invalid. In other words: While the C syntax makes it possible to write "function returning a function", you're not allowed to actually have a function that returns a function in a program. Just like the English language (or any language for that matter) also allows you to express all sorts of thoughts that would physically be impossible to carry out…

Community
  • 1
  • 1
Michael Kenzel
  • 15,508
  • 2
  • 30
  • 39
  • Would that in the former case `*` before the `signal` is still associated first before the last `(int)`? I'm thinking about whether I use this kind of *precedence order* will cause any trouble on other cases. – Kindred Nov 20 '18 at 13:41
  • 1
    Yes, thinking about declarations in terms of precedence of the operators involved is the correct way to go about it. At least according to my understanding. The `()` always binds more strongly than the `*`. So if you have `void *signal()()`, then you read that as "signal is a function that returns a function that returns a pointer to `void`". And if you had `void *signal()()()` then that'd be a function returning a function that returns a function… – Michael Kenzel Nov 20 '18 at 13:47
1

The most important part here is... you don't need to learn this, it is a very poorly designed part of the language. You can scroll down to the bottom of the answer to find the sane, professional solution.

Otherwise, if you insist, it goes like this...


When trying to return a function pointer from a function, the type of the function pointer gets split up. If you want to return a function pointer void(*)(void), then this poor function pointer gets split up in 3 parts. Lets call them like this:

  • void is A, the return type of the pointed-at function.
  • (*) is B, marking this a pointer to function, rather than a function.
  • (void) is C, the parameters of the pointed-at function.

Then if we want to stick this as a return type into some other icky function declaration, they end up like this:

#define A void
#define B *
#define C (void)

// A (B) C equals void(*)(void)

A (B madness(int, void (*fp)(int))) C;

where A, B and C are the parts of our poor function pointer to be returned, madness is the name of the function, and the rest is some mess used as parameters by the function itself.

If we omit the B part, it will be interpreted like a function returning another function of type void f (void); which isn't valid. The syntax allows it but not the language specification.

Similarly, int foo (void) [3]; - a function returning an array, is not allowed either.


Pondering these things is the road to madness and it makes the code unreadable. Professional programmers use typedef.

Given

void (*madness(int, void (*f)(int)))(int);

replace it with:

typedef void func_t (int);

func_t* sanity (int, func_t* f);
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • When you're answering I did find [something](https://stackoverflow.com/a/840504/7813604) you mention at last, and yes I it's the reason I gave up C/C++ before. – Kindred Nov 20 '18 at 14:36