[Q] superfluous ids in self-referential typedef struct

J

J Krugman

My compiler complains if I do something like this

typedef struct {
node *next;
} node;

but it's OK with

typedef struct superfluous_identifier {
superfluous_identifier *next;
} node;

I hate that superfluous_identifier. Is there any way to avoid it?

Thanks!

jill
 
E

Eric Sosman

J said:
My compiler complains if I do something like this

typedef struct {
node *next;
} node;

but it's OK with

typedef struct superfluous_identifier {
superfluous_identifier *next;
} node;

I hate that superfluous_identifier. Is there any way to avoid it?

Yes, but the cure is worse than the disease:

typedef struct {
void *next;
} Node;

You can freely assign `Node*' values to the `next' member
and retrieve them again, but say good-bye to all type-
checking: The compiler will also allow you to assign
and retrieve any other kind of object pointer to `next',
and will not warn you if you do so by accident.
 
M

Martin Ambuhl

J said:
My compiler complains if I do something like this

typedef struct {
node *next;
} node;

but it's OK with

typedef struct superfluous_identifier {
superfluous_identifier *next;
} node;

I hate that superfluous_identifier. Is there any way to avoid it?

typedef struct node node;
struct node {
node *next;
};
 
J

Jens.Toerring

J Krugman said:
My compiler complains if I do something like this
typedef struct {
node *next;
} node;
but it's OK with
typedef struct superfluous_identifier {
superfluous_identifier *next;
} node;
I hate that superfluous_identifier. Is there any way to avoid it?

No. It's required. And it's not really superfluous. You must tell the
compiler how much space it has to set aside for the pointer - the size
of a pointer can differ for e.g. pointers to data objects and functions,
and without telling the compiler which one you need it can't figure out
what to use since it will know what 'node' is typedef'ed to only after
it has found out what defines it. Admittedly, the compiler could be
probably written differently, skipping how the structure is declared,
handling the typedef first and only then going back to find out how
the structure is declared, but then it would be more complicated and
slower. But perhaps

typedef struct node {
struct node *next;
} node;

looks better to you and it's perfectly legal, as well as

typedef struct node node;
struct node {
node *next;
};

As a last resort you could write your own preprocessor, that takes
the file in the form you like it and transforms it to something
the compiler accepts;-)
Regards, Jens
 
C

Chris Torek

My compiler complains if I do something like this

typedef struct {
node *next;
} node;

but it's OK with

typedef struct superfluous_identifier {
superfluous_identifier *next;
} node;

If it is "OK with" this second version, it is not compiling C code
at all. You probably have it set up to compile C++ code -- see
what happens if you write:

#include <stdio.h>

struct A { char c[1000]; };

int main(void) {
struct B { struct A { char c[1]; } a; char c[1]; };

printf("sizeof(struct A): %lu\n", (unsigned long)sizeof(struct A));
return 0;
}

This program is legal in both languages, but is almost certain to
produce different results:

% ln t.c t.c++
% cc -O -o t t.c && ./t
sizeof(struct A): 1
% g++ -O -o t t.c++ && ./t
sizeof(struct A): 1000
%
I hate that superfluous_identifier. Is there any way to avoid it?

Stop using typedef. :)

Seriously, typedef does not really buy you anything here. (Remember,
typedef does not define new types. "struct" defines new types.)
Adding the superfluous typedef-identifier allows you to stop typing
out the keyword "struct". Is it really that hard to type the word
"struct", and a space, each time? Why is:

typedef struct node node;
struct node {
node *next;
/* contents here */
};
node *head;

so much more appealing to some people than:

struct node {
struct node *next;
/* contents here */
};
struct node *head;

? (I prefer the second version myself. I *like* that "struct"
keyword. It is like a cute little kitten -- who could possibly
hate it? :) )
 
K

Keith Thompson

J Krugman said:
My compiler complains if I do something like this

typedef struct {
node *next;
} node;

but it's OK with

typedef struct superfluous_identifier {
superfluous_identifier *next;
} node;

I hate that superfluous_identifier. Is there any way to avoid it?

Let me guess, you're using a C++ compiler, right?

In your first declaration, the name "node" doesn't exist until the
third line, so the reference on the second line is illegal.

In your second declaration, you've created two names for your type:
"struct superfluous_identifier" and "node". The latter is a typedef,
an alias for the type name. In C, struct tags like
"superfluous_identifier" occupy their own name space, and can't be
used without a preceding "struct" keyword. (C++ allows the tag to be
used by itself, but then your code won't compile in C.)

Since struct tags are in a separate name space, you can use the same
identifier for the struct tag and for the typedef:

typedef struct node {
struct node *next;
} node;

In fact, you might consider dropping the typedef altogether, and just
using the struct tag (the typedef name doesn't really add anything):

struct node {
struct node *next;
};

struct node my_object;

By referring to your type as "struct node" rather than just "node",
you make it clear to a reader that the type is a structure. This can
be good or bad, depending on what level of abstraction you're aiming
for. If you want to hide the fact that it's a structure, use a
typedef name. If you're referring to members of the structure, you're
not hiding anything anyway, so you might as well refer to it as
"struct node".

A lot of C programmers use typedefs for structs anyway. It's a matter
of style.
 
C

Chris Croughton

My compiler complains if I do something like this

typedef struct {
node *next;
} node;

With good reason, you're asking it to use an identifier before it knows
anything about the identifier. C compilers are not prophets, they
pretty much work in a linear fashion. Even if it did do some kind of
lookahead you would get into trouble with mutually referencing structs
(A has a pointer to B and B has a pointer to A).
but it's OK with

typedef struct superfluous_identifier {
superfluous_identifier *next;
} node;

Yup, except that the identifier isn't superfluous to the language.
I hate that superfluous_identifier. Is there any way to avoid it?

(a) Use C++, where typedefs share the same name space with structs and
unions. This can cause other problems, for instance the construct

typedef struct node node;
struct node
{
node *next;
};

is invalid in C++ ('node' is delcared twice), so in header files which
have to be includable by both languages the identifiers must be
different (I generally use _s on the end for the struct name and _t or T
for the type, but coding standards vary and there is no One True Naming
Convention).

(b) No, but you can put "syntactic sugar" to cover it up using the
preprocessor:

#define STRUCT(name) typedef struct name##_s name; struct name##_s

STRUCT(Node)
{
Node *next;
...
};

(c) You can use the pre-typedef[1] syntax:

struct node
{
struct node *next;
...
};

[1] I remember when typedef was introduced, I still think of it as a
fairly new and exotic feature. For that matter I remember when void was
indroduced. And somewhere I have a paper tape of the Ocode for the BCPL
compiler, if it hasn't got lost in house moves over the last 30 years...

Chris C
 
C

CBFalconer

J said:
My compiler complains if I do something like this

typedef struct {
node *next;
} node;

but it's OK with

typedef struct superfluous_identifier {
superfluous_identifier *next;
} node;

I hate that superfluous_identifier. Is there any way to avoid it?

It's still wrong if you are using a C compiler. There is no such
type as superfluous_identifier. However there is a type "struct
superfluous_identifier".
 
D

Dave Thompson

With good reason, you're asking it to use an identifier before it knows
anything about the identifier. C compilers are not prophets, they

Although this is really a self-fulfulling prophecy. The language was
defined to be one-pass parseable to simplify the initial compiler(s),
so as a result all compilers now can be and typically are. You can
design a language that doesn't do this and successfully compile it --
the case in point is PL/I. But there doesn't seem to be any great
benefit in doing so, and clearly not very strong demand.
pretty much work in a linear fashion. Even if it did do some kind of
lookahead you would get into trouble with mutually referencing structs
(A has a pointer to B and B has a pointer to A).
With a syntax that unambiguously identifies type names used as targets
like Pascal, or at all like Fortran, you don't need type declaration
first; Ada could do the same, but chose not to. Or with untyped
pointers like PL/I the issue doesn't even arise.
Yup, except that the identifier isn't superfluous to the language.


(a) Use C++, where typedefs share the same name space with structs and

Using C++ only avoids the keyword 'struct', not the 'superfluous
identifier'. And they don't exactly _share_ the namespace, rather tags
are available as typenames. In both languages typedefs share a
namespace with objects and functions and it is still legal in C++,
although arguably more confusing, to have an object or function x and
also a struct/class and/or union and/or enum x in the same scope.
unions. This can cause other problems, for instance the construct

typedef struct node node;
struct node
{
node *next;
};

is invalid in C++ ('node' is delcared twice), so in header files which

No, it's not. Unlike C, C++ allows "benign" redeclarations of types,
that is redeclaration to the "same" type. Perhaps partly to allow this
idiom; I'd have to try to look up in D&E to check. Possibly this
wasn't true in very early C++ (or CwC?); I don't recall trying then.

Much as both C and C++ allow "benign" re-#define-ition of a macro,
albeit C++ attempts to discourage all macros to a significant extent.
have to be includable by both languages the identifiers must be
different (I generally use _s on the end for the struct name and _t or T
for the type, but coding standards vary and there is no One True Naming
Convention).
<rest snipped>

- David.Thompson1 at worldnet.att.net
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top