Ozan Yurtsever
Ozan Yurtsever

Reputation: 1304

Usage of Typedef Struct in C programming

I have a C template which is given me as homework. But before doing homework, I need to understand the usage of "typedef" and "struct" clearly to move on coding. Here is the code;

typedef struct NODE_s *NODE;
typedef struct NODE_s
{
    NODE right;
    NODE left;
    unsigned long data;
    int height;
} NODE_t[1];

typedef struct TREE_s *TREE;
typedef struct TREE_s
{
    NODE root;
} TREE_t[1];

TREE tree_init();
NODE node_init(unsigned long data);

First of all, what is the line typedef struct NODE_s *NODE; is doing here? Why it is named like a pointer with a *?

Then, after the struct definition, what is the purpose of creating a variable named NODE_t[1], is the square brackets with the number "1" something connected with an array, or it is just something else?

Lastly, when the tree_init(); and node_init(unsigned long data); functions are declared, why the TREE and NODE datatype names are used to the contrary they were declared as *TREE and *NODE before the struct definitions? Thank you for the answers.

Upvotes: 1

Views: 672

Answers (1)

Jonathan Leffler
Jonathan Leffler

Reputation: 755074

The template you were given is this, where I've added numbers to some of the lines for ease of reference:

typedef struct NODE_s *NODE;    // 1
typedef struct NODE_s           // 2
{
    NODE right;                 // 3
    NODE left;
    unsigned long data;
    int height;
} NODE_t[1];                    // 4

typedef struct TREE_s *TREE;
typedef struct TREE_s
{
    NODE root;
} TREE_t[1];

TREE tree_init();               // 5
NODE node_init(unsigned long data);

What are the problems here?

  1. As noted in comments, the SO Q&A Is it a good idea to typedef pointers suggests that it is not a good idea to typedef pointers, with limited exceptions for 'pointers to functions' (not relevant here) and perhaps (but probably not) for opaque types. This line does two things: (1) it says "there is a structure type with the tag NODE_s; (2) the name NODE is a synonym for struct NODE_s *. The structure type is incomplete at the moment; no details are known about its members.
  2. This line starts a new typedef, but also starts the definition of the type struct NODE_s (because of the { that follows on the next line).
  3. The type NODE is already known; it can be used here. It means the same as if you wrote struct NODE_s *right;.
  4. The name NODE_t is an alias for the type struct NODE_s[1], an array of 1 struct NODE_s. It isn't clear what this is going to be used for. I have reservations (at best) about its existence. (You can also apply the discussion in points 1, 2, 4 to the struct TREE_s type, mutatis mutandis.)
  5. This is a function declaration, but it is not a prototype declaration. It says that the tree_init() function can be called with any number of arguments of any type because no information is specified about the number or type of those arguments. We do know it is not a variadic function (variable argument list, like printf()) because those must have a full prototype declaration ending with , ...) in scope before they're used. If you want to specify that the function takes no arguments, say so: TREE tree_init(void);.

I think the intent behind the NODE_t and TREE_t types is to allow you to write, for example:

int main(void)
{
    TREE_t x = { 0 };

    if (x->root != 0)
        return 1;
    return 0;
}

I'm not convinced whether that's sufficiently helpful to warrant the type compared with using struct NODE_s x and using x.root in the test. It does save you from having to add an & when passing a pointer to a function; again, I'm not sure it is really sufficiently helpful to warrant its existence.

What I would prefer to see as the template is:

typedef struct NODE_s NODE;
struct NODE_s
{
    NODE *right;
    NODE *left;
    unsigned long data;
    int height;
};

typedef struct TREE_s TREE;
struct TREE_s
{
    NODE *root;
};

extern TREE *tree_init(void);
extern NODE *node_init(unsigned long data);

This removes the pointers from the typedef statements, avoids the somewhat peculiar array types, and uses an explicit prototype for tree_init(). I personally prefer to have function declarations marked with extern; in a header, they'll match the extern on those rare global variables that are declared in the header. Many people prefer not to use extern because the compiler assumes that anyway — so be it; the most important thing is consistency.

The code in main() would now be written:

int main(void)
{
    TREE x = { 0 };

    if (x.root != 0)
        return 1;
    return 0;
}

The difference? An arrow -> changed to a dot .. Not a lot of problem there. However, when calling functions, you'd probably use &x whereas with the TREE_t type, you would just write x (because it's an array).

Upvotes: 4

Related Questions