Parminder Singh
Parminder Singh

Reputation: 362

Is it legal to have a function prototype slightly different from its definition?

//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?

Edit

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);
      |      ^~

Upvotes: 0

Views: 86

Answers (2)

Lundin
Lundin

Reputation: 214770

The problem is that

  • typedef struct my_struct_t *my_handle; and
  • typedef 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.

Upvotes: 5

Sneftel
Sneftel

Reputation: 41522

f1 isn't okay, and shouldn't even compile. my_struct_t is a typedef name, not a struct name. For the same simplistic reason, f2 isn't okay.

If you changed the struct definition in the source to struct my_struct_t { ... }; and changed the function definitions (not the declarations) to take struct my_struct_t then f1 would compile and work fine, but f2 would still fail to compile, because the definition wouldn't match the declaration.

If you didn't let the source file see the header, then there would likely be no compilation or linking error, but the behavior of the code would be undefined, as there would be a violation of the One Definition Rule between the declarations of f2 (one visible to main.c, the other to my_struct.c).

Upvotes: 0

Related Questions