forward declaration of a struct in C?
Asked Answered
S

3

64
#include <stdio.h>

struct context;

struct funcptrs{
  void (*func0)(context *ctx);
  void (*func1)(void);
};

struct context{
    funcptrs fps;
}; 

void func1 (void) { printf( "1\n" ); }
void func0 (context *ctx) { printf( "0\n" ); }

void getContext(context *con){
    con=?; // please fill this with a dummy example so that I can get this working. Thanks.
}

int main(int argc, char *argv[]){
 funcptrs funcs = { func0, func1 };
   context *c;
   getContext(c);
   c->fps.func0(c);
   getchar();
   return 0;
}

I am missing something here. How to fix this?

Senhor answered 3/4, 2012 at 18:50 Comment(1)
C doesn't let you just say context *whatever;, does it? I thought sure it made you say struct context *whatever;...Pucka
H
45

Try this

#include <stdio.h>

struct context;

struct funcptrs{
  void (*func0)(struct context *ctx);
  void (*func1)(void);
};

struct context{
    struct funcptrs fps;
}; 

void func1 (void) { printf( "1\n" ); }
void func0 (struct context *ctx) { printf( "0\n" ); }

void getContext(struct context *con){
    con->fps.func0 = func0;  
    con->fps.func1 = func1;  
}

int main(int argc, char *argv[]){
 struct context c;
   c.fps.func0 = func0;
   c.fps.func1 = func1;
   getContext(&c);
   c.fps.func0(&c);
   getchar();
   return 0;
}
Hat answered 3/4, 2012 at 19:8 Comment(1)
It would be clearer to just say what must be changed in the OP's code.Freeman
G
71

A struct (without a typedef) often needs to (or should) be with the keyword struct when used.

struct A;                      // forward declaration
void function( struct A *a );  // using the 'incomplete' type only as pointer

If you typedef your struct you can leave out the struct keyword.

typedef struct A A;          // forward declaration *and* typedef
void function( A *a );

Note that it is legal to reuse the struct name

Try changing the forward declaration to this in your code:

typedef struct context context;

It might be more readable to do add a suffix to indicate struct name and type name:

typedef struct context_s context_t;
Gaidano answered 6/4, 2015 at 18:50 Comment(2)
Nice! I use the last method you mentioned with slight variation: typedef struct context_s context; Is nice when you know you'll NEVER be using the token "context_s" but will be using "context".Zenger
Thanks, typedef struct MyStruct MyStruct; was the fix.Nieberg
H
45

Try this

#include <stdio.h>

struct context;

struct funcptrs{
  void (*func0)(struct context *ctx);
  void (*func1)(void);
};

struct context{
    struct funcptrs fps;
}; 

void func1 (void) { printf( "1\n" ); }
void func0 (struct context *ctx) { printf( "0\n" ); }

void getContext(struct context *con){
    con->fps.func0 = func0;  
    con->fps.func1 = func1;  
}

int main(int argc, char *argv[]){
 struct context c;
   c.fps.func0 = func0;
   c.fps.func1 = func1;
   getContext(&c);
   c.fps.func0(&c);
   getchar();
   return 0;
}
Hat answered 3/4, 2012 at 19:8 Comment(1)
It would be clearer to just say what must be changed in the OP's code.Freeman
U
2
#include <stdio.h>
struct b; // not needed because:
struct a {
    struct b * pb;  // this member definition also forward declares struct b
    int c;
};

typedef struct a a; // needed for a but not struct a because:
struct b {
    struct a* pa;     // this member definition also forward declares struct a
    a * pa1;
    void * c;
};

int main() {
    printf("Hello, world!");
    return 0;
}

Basically, you never need to forward declare struct b on its own, because it always declares the partial type on the line itself when you use it to perform a pure declaration, so this is redundant code. The only benefit of this type of forward declaration is it can be used with a typedef. In C++, you don't need the typedef because struct and typedefs are in the same identifier namespace, so therefore struct b becomes useful because it now declares b, so you will see it in C++.

The whole point is if that type isn't completed before you use it to declare something that is actually a tentative definition and not a declaration (so struct e f at file/block scope without extern) that isn't a pointer, or if you attempt to dereference the pointer if it is a pointer, then you will get an incomplete type error.

So it's more like it allows you to use an incomplete type. Forget about forward declaration because this isn't a separate action. It's part of the line struct g* h. You never need to use a forward declaration that something else actually requires (unless it's a typedef), because it has a forward declaration part of its own line.

Being able to use an incomplete type allows the type to be completed later on, before it is used. You typically see the benefits of forward declaration explained as not having to include the header containing the full definition in C++ when using a pointer to the type, only needing to do class bar and then bar *, or of course just using class bar* without class bar line, if this particular member is never used.

It doesn't let you use a type with incomplete size as a struct member either (if the size is unknown up to an including this like of code), even if the struct is never used to declare/define a variable. I think this is because the struct is a type, and when you provide the complete definition of a type, which you can only do once, it has to be of a complete size, not incomplete (having a member of unknown size), so the type would be unusable. This struct containing the incomplete struct never be referenced, whereas you can reference an incomplete type with incomplete size as opposed to a complete type with incomplete size (as long as you don't define anything with it), and you can reference, but not deference, (an address can be stored in) a pointer with incomplete type, because it has complete size. You can reference memory so long as its incomplete type doesn't entail an incomplete size. extern struct i j at file/block scope is allowed because it will never be needed unless j is referenced in the code, by which time the type must be complete.

Undies answered 5/5, 2021 at 12:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.