Alternative function pointer syntax accepted by compilers with C11 standard

Viewed 95

I am not sure about this function pointer type declaration syntax, but it works. The syntax is just like declaring a regular old function.

typedef struct Element GetChildCallback(void *userdata, usize parent, usize child_no);

I couldn't find any information on how standard-compliant it is or what drawbacks it could possibly have.

I thought this only worked for typedefs, so I took a step further and found out this also works for regular function parameters:

extern inline void dmpstrn(const char *t, usize n, int printer(const char *fmt, ...));

inline void dmpstrn(const char *t, usize n, int printer(const char *fmt, ...)) {
    usize len = strlen(t) > n ? n : strlen(t);
    printer("\"");
    for (usize i = 0; i < len; i += 1) {
        if (t[i] == '\n') 
            printer("\\n");
        else
            printer("%c", t[i]);
    }
    printer("\"\n");
}

// ...

int main() {
    dmpstrn("Hello\nworld", UINT64_MAX, printf);
}

This however doesn't work for variables

    int printer(const char *fmt, ...) = printf; // Invalid

It's as if the it isn't the function pointer but the actual function, but what does it mean?

2 Answers

Function declarations used as function parameters are implicitly adjusted by the compiler to pointers to the function types.

From the C Standard (6.7.6.3 Function declarators (including prototypes))

8 A declaration of a parameter as ‘‘function returning type’’ shall be adjusted to ‘‘pointer to function returning type’’, as in 6.3.2.1

So for example these two function declarations

void f( void( int ) );

and

void f( void ( * )( int ) );

declare the same one function.

On the other hand (6.3.2.1 Lvalues, arrays, and function designators)

4 A function designator is an expression that has function type. Except when it is the operand of the sizeof operator65) or the unary & operator, a function designator with type ‘‘function returning type’’ is converted to an expression that has type ‘‘pointer to function returning type’’

Here is a demonstrative program.

#include <stdio.h>

typedef void F( int );

F display;

void g( F );

void g( F *f )
{
    int x = 10;
    
    f( x );
}

void display( int x )
{
    printf( "x = %d\n", x );
}

int main(void) 
{
    F *fp = display;
    
    g( fp );
    
    return 0;
}

The program output is

x = 10

Investigate the program.

Pay attention to that for example this typedef

typedef void F( int );

may be equivalently rewritten like

void typedef F( int );
typedef struct Element GetChildCallback(void *userdata, usize parent, usize child_no);

This defines GetChildCallback as the actual "function type", which is different from the pointer to function type. This can be used to declare a function or form the pointer to function type, but not to define a function:

GetChildCallback MyButtonCB;        /* OK, declares function */
GetChildCallback MyResizeCB {}      /* ERROR, bad syntax */
struct Element MyButtonCB(void *, usize, usize) {} /* OK */
GetChildCallback  cb1 = MyButtonCB; /* ERROR, bad function definition */
GetChildCallback* cb2 = MyButtonCB; /* OK */
void add_callback(Button* button, GetChildCallback* cb); /* OK */

When a function type is used as a function parameter, it silently becomes the pointer to function type, just like an array parameter type becomes the element pointer type. So your example

inline void dmpstrn(const char *t, usize n, int printer(const char *fmt, ...)) {

means just the same as

inline void dmpstrn(const char *t, usize n, int (*printer)(const char *fmt, ...)) {

including the fact that printer in the function body is a pointer to function, so for example (&printer) does not act the same as (printer).

Related