This post is about understanding details in two pieces of C code. Why this is ok:
typedef struct _api_t api_t;
typedef void (*api_set) (api_t * api;
int a;
);
typedef int (*api_read) (api_t * api;
);
struct _api_t
{
api_set set;
api_read read;
};
and this isn't
typedef struct _api_t
{
api_set set;
api_read read;
}
api_t;
typedef void (*api_set) (api_t * api;
int a;
);
typedef int (*api_read) (api_t * api;
);
error: unknown type name ‘api_set’, error: unknown type name ‘api_read’
CodePudding user response:
These records
typedef void (*api_set) (api_t * api;
int a;
);
typedef int (*api_read) (api_t * api;
);
are incorrect. The compiler should issue error messages.
It seems you mean the following code
typedef struct _api_t api_t;
typedef void (*api_set) (api_t * api, int a );
typedef int (*api_read) (api_t * api );
struct _api_t
{
api_set set;
api_read read;
};
This code is correct because in this structure definition
struct _api_t
{
api_set set;
api_read read;
};
the names api_set
and api_read
(defined as function pointers in preceding typedefs) are already defined before they are used in the structure definition.
As for the structure definition in the second code snippet then the names api_set
and api_read
used in the structure definition are not yet defined
typedef struct _api_t
{
api_set set;
api_read read;
}
api_t;
So the compiler will issue error messages that these names are not defined.
CodePudding user response:
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);
- with forward declaration
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.