//my_struct.h
typedef struct my_struct_t *my_handle;
void f1(my_handle handle);
void f2(my_handle handle);
//my_struct.c
#include "my_struct.h"
typedef struct
{
int a;
int b;
} my_struct_t;
//Is this definition legal?
void f1(my_struct_t *handle)
{
//bla bla
}
//Is this definition legal?
void f2(const my_struct_t *handle)
{
//bla bla
}
//main.c
#include "my_struct.h"
void g1(my_handle handle)
{
//Is this call legal?
f1(handle);
//Is this call legal?
f2(handle);
}
In this specific case my_handle
is an alias for struct my_struct_t *
so I see no reason why the above code should be illegal.
GCC won't let me compile this code, so I guess it's illegal.
What does the ISO c99 standard say about this?
These are the errors that GCC raises: https://onlinegdb.com/6hg40rTsV
my_struct.c:11:6: error: conflicting types for ‘f1’; have ‘void(my_struct_t *)’
11 | void f1(my_struct_t *handle)
| ^~
In file included from my_struct.c:1:
my_struct.h:5:6: note: previous declaration of ‘f1’ with type ‘void(struct my_struct_t *)’
5 | void f1(my_handle handle);
| ^~
my_struct.c:17:6: error: conflicting types for ‘f2’; have ‘void(const my_struct_t *)’
17 | void f2(const my_struct_t *handle)
| ^~
In file included from my_struct.c:1:
my_struct.h:7:6: note: previous declaration of ‘f2’ with type ‘void(struct my_struct_t *)’
7 | void f2(my_handle handle);
| ^~
The problem is that
typedef struct my_struct_t *my_handle;
andtypedef struct { ... } my_struct_t;
don't refer to the same type.
The first declares a typedef my_handle
which is a forward declaration of a pointer to struct my_struct_t
a struct with a tag - ok in itself. The second declares a typedef my_struct_t
which is a typedef for a different struct with no tag.
At no place in the code do you define struct my_struct_t
. Struct tags live in a namespace of their own called the tag namespace, whereas typedef
live in the ordinary namespace. (C and C++ are different here.)
The fix is rather trivial, simply add the same tag name as used in the forward declaration to the struct definition:
typedef struct my_struct_t
{
int a;
int b;
} my_struct_t;
A much better solution however is to never hide pointers behind typedef, because it only confuses the programmer, thinking they are passing something by value when they aren't.
And even better yet, don't use 2 different names for the same thing because the only thing you achieve with that is also to confuse the programmer. Why do you have something called my_handle in a header called my_struct? That doesn't make sense.
// my_struct.h
typedef struct my_struct_t my_struct_t;
// my_struct.c
struct my_struct_t
{
int a;
int b;
};
void f1(my_struct_t* handle);
void f1(my_struct_t* handle)
{
}
And now as it happens, this is the design pattern known as "opaque type" - the way to do private encapsulation in C.