简体   繁体   中英

Define struct field with circular reference

I have the following code where a struct field depends on a struct field that is not declared yet, because that struct in turn might depend on the struct that I am trying to declare.

How can I design this so that it compiles? Or am I missing an obvious way to get rid of the union struct for keeping several struct definitions together?

typedef struct expression {
    expression_type type;
    union {
        bool_expression bool;
        identifier_expression ident;
        integer_expression _int;
        prefix_expression prefix;
        infix_expression infix;

        // TODO: Fix this
        if_expression _if;
    };
} expression;

typedef struct statement {
    token token;
    identifier name;
    expression * value;
} statement;

typedef struct block_statement {
    token token;
    statement *statements;
} block_statement;

typedef struct if_expression {
    token token;
    expression condition;
    block_statement *consequence;
    block_statement *alternative;
} if_expression;

You can't, in this case.

if_expression contains an expression which in turn contains an if_expression . If you solve this problem by using pointers, you can reorder your definitions and use struct expression* to create a pointer to a struct that has not yet been defined.

If you change expression condition; to expression *condition; in if_expression , and forward declare expressiontypedef struct expression expression; at the top and then place struct expression { … }; at the bottom, the code should be OK.

/* Placeholder typedefs for undefined typedef names */
typedef int token;
typedef int identifier;
typedef int bool_expression;
typedef int identifier_expression;
typedef int integer_expression;
typedef int prefix_expression;
typedef int infix_expression;
typedef int expression_type;

/* The fixed code starts here */

typedef struct expression expression;

typedef struct statement
{
    token token;
    identifier name;
    expression * value;
} statement;

typedef struct block_statement
{
    token token;
    statement *statements;
} block_statement;

typedef struct if_expression
{
    token token;
    expression *condition;
    block_statement *consequence;
    block_statement *alternative;
} if_expression;

struct expression
{
    expression_type type;
    union
    {
        bool_expression bool;
        identifier_expression ident;
        integer_expression _int;
        prefix_expression prefix;
        infix_expression infix;
        if_expression _if;
    };
};

expression expr = { 0 };

This compiles.

You better remove the typedefs , at least initially, so you can better understand your types.

You should change your structs to point (rather than include) other structs, for example:

struct if_expression {
    /* struct? */ token *token;
    struct expression *condition;
    struct block_statement *consequence;
    struct block_statement *alternative;
}

Of course that will impose some judiciously use of malloc() and free() calls. In some cases you can later revert to inclusion (like token above, as it isn't expected that it points to your other structs, and copying tokens around is - perhaps - a cheap operation).

For the circularity of references you can forward declarations in C, that is you tell the compiler "hey, there is a struct named expression, whose internal structure you don't know yet, but bear with me, you will soon":

struct expression;

If you later decide to use typedef - so your code gets more streamlined - bear in mind that the structs can have a name, and typedefs define names, and those names don't share the same namespace. In examples:

typedef struct {
    /* ...internal structure omited... */
} type_a;

typedef struct type_b {
    /* ...internal structure omited... */
} type_b;

typedef struct struct_c {
    /* ...internal structure omited... */
} type_c;

Here type_a is a type whose definition is an anonymous struct. And type_b is a type whose definition is struct type_b , that is a named ( type_b ) structure. And type_c is a type whose definition is struct struct_c , that is a named ( struct_c ) structure.

Now put it all together, you can:

/* forward all your structs */
struct expression;
struct if_expression;
struct block_statement;

/* typedef them all */
typedef struct expression expression;
typedef struct if_expression if_expression;

/* actually define them */
struct expression {
    /* ... */
}

As a footnote, you can learn a lot about C language by understanding the difference between declaration and definition.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM