0

I would like to forward declare a typedef struct, to use it in another struct, and then to implement the original struct.

I have tried the following code but does not compile.

struct _s1;
struct _s2;

typedef struct _s1 s1;
typedef struct _s2 s2;

typedef struct _big_struct {
    s1 my_s1;
    s2 my_s2;
} big_struct;

struct _s1 {
    int i1;
};

struct _s2{
    int i2;
};

Any idea?

Nisba
  • 3,210
  • 2
  • 27
  • 46

2 Answers2

4

You can only forward declare the existence of a type and then use a pointer to it. This is because the size of a pointer is always known, while the size of a forward declared compund type is not, yet.

struct s1;
struct s2;

struct big_struct {
    struct s1* pmy_s1;
    struct s2* pmy_s2;
};

struct s1 {
    int i1;
};

struct s2{
    int i2;
};

Note, because of my background, I am used to writing extremely backward-compatible code.
Jonathan Leffler has provided information on need/not-need in more modern versions of the C standard. See the comments below.

Yunnosch
  • 26,130
  • 9
  • 42
  • 54
  • Uh for very few seconds you are first :) Happy that someone else had proposed the same solution – Matteo Ragni Sep 03 '17 at 10:51
  • That is true. :-) Let's race. – Yunnosch Sep 03 '17 at 10:52
  • thank you, the explanation about the memory made me clear of why I can't – Nisba Sep 03 '17 at 10:52
  • If you use the `struct s1` notation in `big_struct`, you don't need the explicit `struct s1` — the use in the definition of `big_struct` is sufficient. If you had `typedef struct s1 s1;` you could then use `struct big_struct { s1 *pmy_s1; … };`. – Jonathan Leffler Sep 03 '17 at 15:57
  • @JonathanLeffler Thanks for the input. Am I right that my code is more backward compatible to older C standards? (My job forces me to think some 20 years back....). – Yunnosch Sep 03 '17 at 16:02
  • 1
    What you wrote works — C90, pre-standard C, as well as C99 or C11. It isn't wrong. OTOH, it isn't strictly necessary in any version of C either. The one time when the plain `struct tag;` notation helps is a case that isn't one you'd normally want to exploit. Inside a function (not at global scope), using `struct s1; struct s2;` like that tells the compiler "there are new structure types `struct s1` and `struct s2` that are unrelated to any previous definition". But this only works at function scope, so you can't pass such structures around between functions reliably. _[…continued…]_ – Jonathan Leffler Sep 03 '17 at 16:07
  • 1
    _[…continuation — 2nd edition…]_ See [Which part of the C standard allows this code to compile?](https://stackoverflow.com/questions/12200096/) for an important part of my education on the topic. At global scope, all references `struct s1` refer to the same type — and you can only define the 'body' of the structure type once. If you don't have the `typedef struct s1 s1;` lines, then you must use `struct s1 *pmy_s1;` in the big structure rather than `s1 *pmy_s1;` (in C — the rules are different in C++, and the plain `struct s1;` forward declaration is preferable in C++ with no typedef needed). – Jonathan Leffler Sep 03 '17 at 16:16
  • @JonathanLeffler Thanks. In order to make my answer clear, I will explicitly state it is **very** backward compatible and to see your comments for more elegant way in more modern C. Would you consider that appropriate? – Yunnosch Sep 03 '17 at 16:20
  • That's fine — if you want to do that much. Or we could simply delete this comment and your 'thanks' comment. Up to you. – Jonathan Leffler Sep 03 '17 at 16:22
  • I appreciate input on modern C and consider it an improvement of the answer. Please keep your informative comments. Feel free to replace in my answer my reference to your comments by the content of your comments, if you like to cleanup. I propose to lead by "Edit Jonathan Leffler on more modern C". – Yunnosch Sep 03 '17 at 16:25
2

If you are really forced with that order (I don't care why), one thing that comes in my mind to have it compile is by making the struct _big_struct entries pointers:

typedef struct s1 s1;
typedef struct s2 s2;

typedef struct _big_struct {
    s1 *my_s1;
    s2 *my_s2;
} big_struct;
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Matteo Ragni
  • 2,837
  • 1
  • 20
  • 34
  • The standalone `struct _s1;` lines provide no value; the following `typedef` lines on their own are sufficient. The names with the leading underscore are treading on delicate territory — generally, it is simplest (best) to assume that leading underscores are reserved for the implementation. The full story is defined in §7.1.3 Reserved identifiers of the standard and is a bit more nuanced, but if you go with 'leading underscores are reserved for the implementation; don't invent your own names with leading underscores', you won't go far wrong. – Jonathan Leffler Sep 03 '17 at 15:58
  • It was copied from the code of the OP. Actually I did not care about those line. Now in the post there is only the interesting lines. Thank you – Matteo Ragni Sep 03 '17 at 16:09