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?
- 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.
- 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).
- The type
NODE
is already known; it can be used here. It means the same as if you wrote struct NODE_s *right;
.
- 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.)
- 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).