4

I'm a little confused by the textbook I'm using compared to examples, SO answers, and tutorials I've found online.

The code from the book declares two function pointers but never assigns a value to them, and it doesn't use * in the declaration. The code runs fine and compiles without warning, so I'm a little confused.

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{   
    void    f(int), g(int);

    signal(SIGUSR1, f);
    signal(SIGUSR2, g);

    while(1)
        sleep(1);
}   

void f(int signum)
{   
    printf("Recieved signal %d: SIGUSR1\n", signum);
}   

void g(int signum)
{   
    printf("Received signal %d: SIGUSR2\n", signum);
    exit(SIGUSR2);
}   

The code that I've found online all looks similar to this, with pointer syntax * and an explicit assignment of a function address to the function pointers:

#include <stdio.h> 

void fun(int a) 
{ 
    printf("Value of a is %d\n", a); 
} 
  
int main() 
{ 
    void (*fun_ptr)(int) = &fun; 
  
    (*fun_ptr)(10); 
  
    return 0; 
} 

Is there more than one correct way to declare a function pointer?

Why does the code from the book work?

Is one version "more correct" than the other?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Jonathon Anderson
  • 1,162
  • 1
  • 8
  • 24
  • 2
    The first one is a [forward declaration](https://stackoverflow.com/q/9186996/11683). The second one is a function pointer. – GSerg Jul 09 '20 at 19:36
  • @GSerg So, in this case, `void` is return type, not a type declaration? – Jonathon Anderson Jul 09 '20 at 19:41
  • Also, wouldn't I still need to assign the address of `f` and `g` in order to pass them as arguments? – Jonathon Anderson Jul 09 '20 at 19:42
  • 1
    `f` and `g` are not variables, they cannot be assigned to. Would it be more understandable if `void f(int); void g(int);` were located right before `int main()`? – GSerg Jul 09 '20 at 19:45
  • @GSerg Yea, that is more clear, aside from the fact that I thought functions had to be passed as pointers, and not just throwing function names in. – Jonathon Anderson Jul 09 '20 at 19:46
  • https://stackoverflow.com/q/39806309/11683 – GSerg Jul 09 '20 at 19:49
  • Thanks. With that and some more `Google-Fu` it makes sense – Jonathon Anderson Jul 09 '20 at 19:56
  • You can not safely call `printf()` or `exit()` from within a signal handler. [Footnote 188 of the C11 standard](https://port70.net/~nsz/c/c11/n1570.html#note188) says: "Thus, a signal handler cannot, in general, call standard library functions." [POSIX allows for the calling of async-signal-safe functions](https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03), but neither `printf()` nor `exit()` are async-signal-safe. [Windows has similar if less-well-defined restrictions](https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/signal?view=vs-2019) – Andrew Henle Jul 09 '20 at 20:10
  • @AndrewHenle Thanks for the input. I've run into a lot of issues like this with the text book I'm using. Every time I've asked a question about a sample, I get a response about how the code is technically wrong in some way.... `Systems Programming with C and Unix - Adam Hoover` – Jonathon Anderson Jul 09 '20 at 20:13

2 Answers2

3

void f(int), g(int); doesn't declare function pointers*. It declares two functions (f taking int and returning void, and g taking int and returning void).

(void (*f)(int), (*g)(int); would declare the corresponding function pointers and those would indeed require some initialization before use.)

Declaring functions in a block scope is a little bit confusing because functions cannot be block-local.

In effect it's as if you had void f(int); void g(int); in filescope, except the block scope limits the scope of the forward declarations:

Example:

void call_f(void)
{
    void f(int);
    f(42);
}

void call_f_again(void)
{
    void f(int); //need to redeclare unless there's also a global decl for f
    f(42);
}

but this scope limitation doesn't really do anything when it comes to functions as all such redeclarations have to be compatible with each other and with a corresponding filescope declaration (whether or not it's provided).

It's simplest (and least confusing) to simply declare functions at filescope.


  • void f(int) is only equivalent to void (*f)(int) when it's used as a function paramater. This is similar to how int f[] in a function parameter is equivalent to int *f.
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • How am I able to pass the functions as arguments without having some sort of assignment? I thought functions had to be passed as pointers when used as an argument. Can I always just throw the function name in without first creating a function pointer? – Jonathon Anderson Jul 09 '20 at 19:45
  • @JonathonAnderson Functions very readily decay to pointers (just like arrays do) so an `&` isn't really required to turn a function identifier into a function pointer. It's similar with `*` before function pointers when calling them (see e.g., https://stackoverflow.com/a/54962440/1084774 or other materials on StackOverflow for more info). – Petr Skocik Jul 09 '20 at 19:52
  • 1
    Makes sense. Thank you! – Jonathon Anderson Jul 09 '20 at 19:56
1

This declaration

void    f(int), g(int);

declares two functions f and g in the block scope of the function main. They could be declared before main but the author of the code decided to declare them in main because they are used only in main. So there is no declarations of function pointers.

It seems you are confused by the place where the functions are declared.

To make it more clear you could rewrite the program the following way

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>

void    f(int), g(int);

// or

void f(int);
void g(int);

int main(int argc, char *argv[])
{   
    signal(SIGUSR1, f);
    signal(SIGUSR2, g);

    while(1)
        sleep(1);
}   

void f(int signum)
{   
    printf("Recieved signal %d: SIGUSR1\n", signum);
}   

void g(int signum)
{   
    printf("Received signal %d: SIGUSR2\n", signum);
    exit(SIGUSR2);
}

The program is valid though the compiler can issue a message that there are redundant function declarations.

The function designators used in these statements

signal(SIGUSR1, f);
signal(SIGUSR2, g);

are implicitly converted to pointers to the functions by the compiler.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335