Is it possible to consistently refer to forward-declared and non-forward-declared structs in C?

114 views Asked by At

This doesn't work for foo

struct Foo;

typedef struct
{
    int x;
}
Bar;

void foo (Foo *); // unknown type name ‘Foo’

void bar (Bar *);

typedef struct
{
    int y;
}
Foo;

This doesn't work for bar

struct Foo;

typedef struct
{
    int x;
}
Bar;

void foo (struct Foo *);

void bar (struct Bar *); ‘struct Bar’ declared inside parameter list

typedef struct
{
    int y;
}
Foo;

Some of my structs have to be forward-declared, because they are passed as pointers, and some of them have to be not forward-declared, because they are passes as values.

Is there a way to declare types in C such that all function prototypes can consistently always refer to custom types in the same way, regardless of whether they are forward-declared or not?

5

There are 5 answers

0
melpomene On BEST ANSWER

Your problem is not forward-declared vs. non-forward-declared, it's struct X vs. typedef struct { ... } X.

You can solve this by using struct X only:

struct Foo;

struct Bar
{
    int x;
};

void foo (struct Foo *);
void bar (struct Bar *);

struct Foo
{
    int y;
};

Once you have that, you can introduce typedef names:

typedef struct Foo Foo;

typedef struct
{
    int x;
}
Bar;

void foo (Foo *);
void bar (Bar *);

struct Foo
{
    int y;
};

You can't pre-declare typedefs, so we still need a real struct type we can forward to. There's a small inconsistency here: Foo is the same as struct Foo, but there is no struct Bar, only Bar. You can fix that by switching to

typedef struct Bar
{
    int x;
}
Bar;

This defines struct Bar and Bar simultaneously.

0
Stargateur On
struct Foo;

Declare a structure named Foo. But, you must declare a typedef Foo of structure named Foo.

#include  <stdio.h>

typedef struct Foo Foo;

typedef struct {
    int x;
} Bar;

void foo(Foo *);

void bar(Bar *);

struct Foo {
    int y;
};

int main(void) {
    printf("Hello, world!\n");
}
1
StoryTeller - Unslander Monica On

The first order of business is to point out that in your examples there is no struct Foo. There is only a struct without a tag that you typedef as Foo.1

Is there a way to declare types in C such that all function prototypes can consistently always refer to custom types in the same way, regardless of whether they are forward-declared or not?

The definition or declaration of the struct should appear before the struct is used in a function parameter list. Otherwise the declarations scope is only the function prototype, which is almost certainly not what you want.

The way to achieve what you want is only through disciplined coding.

A forward declaration is all you ever really need for a function prototype. You don't need the full struct definition until the function itself is defined or called.

struct Foo;
void foo (struct Foo); // Okay, only at the call site or definition site
                       // do we need the full struct definition.

A short example to demonstrate

#include <stdio.h>

struct Foo;
void foo (struct Foo);

struct Foo
{
    int y;
};

int main(void) {

    struct Foo f = { .y = 1 };
    foo(f);

    return 0;
}

void foo (struct Foo f)
{
    printf("%d", f.y);
}

It's much clearer that it's okay when the definitions and declarations are spread out among different translation units, but the above will have to do.

So what I suggest to you, is that you always forward declare your structs on a separate line, before they are to be used in function prototypes (by pointer or by value). And refrain from introducing the full definition until it's really needed.


1 A lovely correspondence on fa.linux.kernel where Linus Torvalds articulates far better than me why you should prefer to use the full struct tag.

6
Amin Negm-Awad On

You mix up forward declaration and type definitions. Both are different things. If you want to have the same kind of usage of forward declared structs do it:

struct Foo;             // forward declaration
struct Bar { int x; };  // definition

void foo(struct Foo);   // works for a prototype
void bar(struct Bar);   // works for a prototype

You cannot forward declare type definitions. Maybe this is the reason for the name definition. However, you can type define a forward declaration:

struct Foo;                // forward declaration
typedef struct Foo Foo_t;  // type definition of a forward declaration
struct Bar { int x; };     // definition
typedef struct Bar Bar_t;  // type definition of a definition

void foo(Foo_t);           // works
void bar(Bar_t);           // works

If you have a function definition the type has to be completed. This means that you have already defined the forward declared type at this location or that you have to use pointers. Again: It does not matter, whether you use structures or type definitions.

BTW: As you can see in my examples, it is an urban legend that incomplete structure types has to be passed "by reference". You can use the incomplete type in the function declaration (prototype). However, when you define the function, the type has to be completed. This is no deal in most cases.

1
Paul Ogilvie On

The only "forward declaration" and reference supported by C is the pointer, e.g.:

void myFunc (struct XYZ *p);

This tells the compiler you are referring to a type struct XYZ, possibly not yet declared, of which you are passing a pointer. This allows the compiler to perform type checking without knowing what this structure actually consists of.

Note that pointers are all the same size, so there is no check on the pointer itself, just on the thing it is pointing to.

See also other solutions that explain using typedef.