About the semicolons as separator of function parameters:
typedef void (*api_set) (api_t * api; int a;);
This accidentally "works" because there is GCC extension allowing forward declarations of function parameters.
If you run the compilation in the "pedantic" mode then you will see the warning:
warning: ISO C forbids forward parameter declarations [-Wpedantic]
The forward declarations of parameters is useful for functions taking Variable-Length Arrays (VLAa) as parameters because it allows to pass a parameter for array size after the parameter for an array.
- without forward declaration
void foo(int n, int arr[static n]);
// calling example
int A[3];
foo(3, A);
void foo(int n; int arr[static n], int n);
// calling example
int A[3];
foo(A, 3);
This feature was proposed to upcoming C23. See https://www9.open-std.org/JTC1/SC22/WG14/www/docs/n2780.pdf
In your code, the two parameters api
and a
are forward declared though never actually used. As result those forward declarations are ignored and the full declaration is equivalent to:
typedef void (*api_set) ();
Which a pointer to a function taking unspecified number of arguments and returning void
.
This function can be called with any number of arguments without raising a warning from a compiler. Moreover, this kind of type is incomplete as it is compatible with any function returning void
independently from number and types of arguments used.
To fix the issue use ,
as a separator:
typedef void (*api_set) (api_t *api, int a);
Moreover, consider creating an alias for a function type rather than function pointer. It usually results in a clear code:
typedef struct _api_t api_t;
typedef void api_set_f (api_t *, int);
typedef int api_read_f (api_t *);
struct _api_t {
// declare *pointer* to functions
api_set_f* set;
api_read_f* read;
};
In the second example the structure struct _api_t
contains a member of type api_set
, however this type is not defined at this stage of parsing.