1

There's a C function declaration signal from c-faq website example 3:

void (*signal(int, void (*fp)(int)))(int);

I wonder how can there be two identifiers signal, fp and it still passes compilation (I added this line randomly in my existing code and it compiles successfully)?

Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
mzoz
  • 1,273
  • 1
  • 14
  • 28
  • [Does redefining a function from the standard library violate the one-definition rule?](https://stackoverflow.com/questions/41670800/does-redefining-a-function-from-the-standard-library-violate-the-one-definition) – David C. Rankin Feb 24 '20 at 04:03
  • 1
    `void (*signal(int, void (*)(int)))(int);` is a [function (int, pointer to function (int) returning void) returning pointer to function (int) returning void](https://cdecl.org/?q=void+%28*signal%28int%2C+void+%28*%29%28int%29%29%29%28int%29%3B) – chux - Reinstate Monica Feb 24 '20 at 04:20

2 Answers2

3

The name fp is commonly used for variables of type FILE *, but it could be used for all sorts of other purposes (floating-point, fishery protection, function pointer, …).

The function declaration in the question is equivalent to the declaration of signal() in the C standard except that it uses fp where the standard uses func, and the standard gives the name sig to the first argument to signal().

You could delete the fp from the function declaration (prototype); nothing significant changes:

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

You could also use meaningful names for arguments throughout:

void (*signal(int signum, void (*handler)(int signum)))(int signum);

Each occurrence of signum identifies where a signal number is provided. The first argument to signal() itself is a signal number; the argument to handler indicates that the handler function will be called with the signal number; and the outermost signum indicates that the function pointer returned by signal() is of the same type as handler and should be (is) passed a signal number when called.

See also Understanding typedefs for function pointers in C.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
1

No, a C function declaration/prototype cannot have any more than one identifier. Refer to the reference page and example code for signal on cplusplus.com here: http://www.cplusplus.com/reference/csignal/signal/.

In this declaration:

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

we are declaring a function called signal which returns a pointer to a function which returns void and accepts an int--ie: a pointer to a function like this:

void func(int);

The inputs to the signal function are 1st, an int, and 2nd, a pointer to a function which returns a void and accepts an int--again, a pointer to a function like func above. Now, in C, when declaring a function which returns a pointer to a function and accepts a pointer to a function, it gets all goobly-ga-gook-looking (garbled and confusing looking), which is why we all have confusion initially (and again and again frequently) looking at function declarations like signal. A couple much much much clearer ways to define a function like signal in C, to make this all crystal-clear, are like this, instead, both of which are identical to the goobly-gook-looking one:

// 1. typedef a function--call it `func_t` for "func type"
typedef void func_t(int);

// 2. Use the typedef above to define `signal`
func_t* signal(int sig, func_t* fp); // Ah, now this makes sense!

OR

// 1. typedef a *pointer to* a function--call it `func_p` for "pointer to a func type"
typedef void (*func_p)(int);

// 2. Use the typedef above to define `signal`
func_p signal(int sig, func_p fp); // Ah, now this makes sense too!

And remember, what you call each input parameter type in a function declaration is irrelevant in C and doesn't even have to match between a header and source file.

Ex:

my_module.h:

// Any of these 3 prototypes are equivalent, valid, and identical
// (although using sensible names which match between the header &
// source files is most clear and helpful to the reader!):
func_p signal(int sig, func_p fp);
func_p signal(int signal, func_p whatsupdude);
func_p signal(int, func_p);

my_module.c:

func_p signal(int sig, func_p fp)
{
    // define the function here
}

Now, how did I initially figure all this out?
Ans: I looked at the example code from cplusplus.com. It helped a TON to make this all clear, as you can see how the return value from signal is assigned to prev_handler, which is a pointer to a function, and you can see how my_handler, which is defined as a function, is passed in as the 2nd parameter to signal! For kicks, I should probably also mention that these 2 lines are identical, and both are perfectly valid:

prev_handler = signal (SIGINT, my_handler);
prev_handler = signal (SIGINT, &my_handler);

This is because if you pass in a function as a parameter the compiler knows to just take its address anyway.

Cplusplus example code:

/* signal example */
#include <stdio.h>      /* printf */
#include <signal.h>     /* signal, raise, sig_atomic_t */

sig_atomic_t signaled = 0;

void my_handler (int param)
{
  signaled = 1;
}

int main ()
{
  void (*prev_handler)(int);

  prev_handler = signal (SIGINT, my_handler);

  /* ... */
  raise(SIGINT);
  /* ... */

  printf ("signaled is %d.\n",signaled);


  return 0;
}

References:

  1. Reminder about how to typedef functions and function pointers, since this is confusing to pretty much everybody and requires re-verifying from time to time: http://www.iso-9899.info/wiki/Typedef_Function_Type
Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265