Could you explain this typedef to me?

J

James Kuyper

On 02/03/2014 11:59 AM, Robbie Brown wrote:
....
So now I appear to have 4 different ways to declare/define a struct.

You need to distinguish between defining a struct type, and defining an
object of struct type:

// Defining struct types
struct tag1 { ... };
typedef struct { ... } name1;
typedef struct tag2 { ... } name2;

tag1 and tag2 are struct tags. "struct tag1", name1, "struct tag2", and
name2 are type names; the last two are different type names for the same
exact type. Note: they don't have to be different, and it is in fact
common practice to have the typedef name match the corresponding struct
tag - doing so reduces some of the differences between C and C++.

// Defining objects of struct type
struct tag1 obj2;
name1 obj3;
struct tag2 obj4;
name2 obj5;

// Defining a struct type and an object of that type in the same
// definition:
struct tag3 { ... } obj6;
struct { ... } obj7.

Does that clear it up?
 
R

Robbie Brown

snip


Well that's a start. What C book is it? What is it about its
explanation of struct types that is not clear?

Right, let's see if we can nip this in the bud.

I'm currently reading four publications concerning C

Understanding and Using C Pointers
by Richard Reese

The Linux Programming interface
A Linux and UNIX System Programming Handbook
by Michael KerrisK

Expert C Programming: Deep C Secrets
By Peter van der Linden

and of course the obligatory K&R (ANSI C version)

Personally I enjoy the social interaction of discussion about all sorts
of things. I find I learn just as much by talking to people as I do by
reading. I was simply discussing something that I find interesting.

I have a standard way of dealing with posts I find irritating or
irrelevant, I simply don't reply. I don't like arguing, it's a waste of
time and resolves nothing.

I've learned a lot about structs thanks to this discussion and have made
notes on the points raised.

I hope this clears things up and thank you for your time.
 
R

Robbie Brown

[...]
So now I appear to have 4 different ways to declare/define a struct.
I wonder how many more there.

I'm not sure what your "4 different ways" are.

I have four struct declarations in my 'learning about structs' file

typedef struct Foobar{
int a;
int b;
}Foobar, *fbar;

struct Barbaz{
int a;
int b;
}Barbaz, *bbaz;

typedef struct{
long x;
long y;
}AArdvark;

struct{
char *cp1;
char *cp2;
}Whoami;

I have the following 'usages' of these structs

Foobar fb;
fb.a = 1;

fbar fbp = malloc(sizeof *fbp);

Barbaz.a = 2;
bbaz = malloc(sizeof *bbaz);

AArdvark a;
a.y = 8;

AArdvark *ap = malloc(sizeof *ap);
ap -> y = 9;

Whoami.cp1 = "foobarbaz";

This all compiles although that's all I've done, not otherwise tested.

snip
I get the impression that you're floundering.

Not at all, I'm having enormous fun, making mistakes and learning.
 
B

BartC

James Kuyper said:
On 02/03/2014 11:59 AM, Robbie Brown wrote:
...

You need to distinguish between defining a struct type, and defining an
object of struct type:

// Defining struct types
struct tag1 { ... };
typedef struct { ... } name1;
typedef struct tag2 { ... } name2;

tag1 and tag2 are struct tags. "struct tag1", name1, "struct tag2", and
name2 are type names; the last two are different type names for the same
exact type. Note: they don't have to be different, and it is in fact
common practice to have the typedef name match the corresponding struct
tag - doing so reduces some of the differences between C and C++.

// Defining objects of struct type
struct tag1 obj2;
name1 obj3;
struct tag2 obj4;
name2 obj5;

// Defining a struct type and an object of that type in the same
// definition:
struct tag3 { ... } obj6;
struct { ... } obj7.

Does that clear it up?

Not really. C has all these extra aspects to defining struct types that
other languages that have records don't have and don't appear to need. And
with so many extra possibilities, together with the concept of 'tag' names
(another new idea) having their own namespace, it can get confusing for
beginners (for others too!). Trying to explain it doesn't make it any
simpler, or code that mixes all these up less confusing.

So I try to:

- Always use typedefs to wrap struct types

- Not use struct tags

- Not use anonymous structs

(But with some exceptions)
 
K

Keith Thompson

Robbie Brown said:
[...]
So now I appear to have 4 different ways to declare/define a struct.
I wonder how many more there.

I'm not sure what your "4 different ways" are.

I have four struct declarations in my 'learning about structs' file

typedef struct Foobar{
int a;
int b;
}Foobar, *fbar;

struct Barbaz{
int a;
int b;
}Barbaz, *bbaz;

typedef struct{
long x;
long y;
}AArdvark;

struct{
char *cp1;
char *cp2;
}Whoami;

Those are not 4 different ways *of doing the same thing*.

The first defines a struct type, an alias for the struct type, and an
alias for the corresponding pointer-to-struct type; it defines no
objects.

The second defines a struct type, an object of the struct type, and an
object of the corresponding pointer-to-struct type.

The third defines an anonymous struct type and an alias for that type
(again, it defines no objects).

The fourth defines an object of an anonymous struct type.

The unqualified word "struct" or "structure" is commonly used either to
refer to a struct type or to an object of some struct type. For
clarity, I suggest avoiding using the word "struct" or "structure" by
itself, instead using it as an adjective: "struct type", "struct object"
(also "struct expression", an expression of struct type, and "struct
value", a value of some struct type). Among other benefits, that would
probably have avoided the confusion of thinking of those 4 snippets as
"4 different ways to declare/define a struct".

[...]
 
E

Eric Sosman

[...]
So now I appear to have 4 different ways to declare/define a struct.
I wonder how many more there.

I'm not sure what your "4 different ways" are.

I have four struct declarations in my 'learning about structs' file

typedef struct Foobar{
int a;
int b;
}Foobar, *fbar;

This declares `struct Foobar' in one of the four-minus-one
ways I listed: With a tag and with its details (its "completion").
It also declares `Foobar' as an alias for `struct Foobar' and
`fbar' as an alias for `pointer to struct Foobar', but those are
just aliases, not declarations of a struct type.
struct Barbaz{
int a;
int b;
}Barbaz, *bbaz;

This declares `struct Barbaz' in the same way `struct Foobar'
was declared: With a tag and with its innards. It also declares
a variable `Barbaz' as an instance of `struct Barbaz' and a
variable `bbaz' as an instance of a pointer to a `struct Barbaz',
but again: Those are just variables, not declarations of a struct.
typedef struct{
long x;
long y;
}AArdvark;

This declares a struct that is untagged and complete, which
is another of the ways I listed. It also declares `AArdvark' as
an alias for that struct type.
struct{
char *cp1;
char *cp2;
}Whoami;

Same thing again: This declares an untagged complete struct
type. It also declares one variable of that type.

So your "four ways" actually cover two of the three possible
ways to declare a struct type, twice each. For completeness, the
third way is

struct Opaque;

.... which declares a tagged *in*complete struct type. This way
could also appear as

typedef struct Opaque DirtyWindow;
struct Opaque *makeOpaque(void);
struct Opaque *kierkegaard = makeOpaque();
// etc.

.... but all of these are just uses of the type, not declarations
of it.
Not at all, I'm having enormous fun, making mistakes and learning.

I get the impression that you're floundering.
 
B

BartC

Those are not 4 different ways *of doing the same thing*.

The first defines a struct type, an alias for the struct type, and an
alias for the corresponding pointer-to-struct type; it defines no
objects.

The second defines a struct type, an object of the struct type, and an
object of the corresponding pointer-to-struct type.

The third defines an anonymous struct type and an alias for that type
(again, it defines no objects).

The fourth defines an object of an anonymous struct type.

Four ways of creating a 'struct type' though. The following all do the same
thing: defining a variable d1, d2 etc which is a struct of 3 integers (I've
had to give each name a different numeric suffix just so I could compile
them all together):

struct {int day,month,year;} d0;

struct date1 {int day,month,year;} d1;

struct date2 {int day,month,year;};
struct date2 d2;

typedef struct {int day,month,year;} Date3;
Date3 d3;

typedef struct date4 {int day,month,year;} Date4;
Date4 d4a;
struct date4 d4b;

The last two variables d4a and d4b are compatible. So four or five ways of
specifying the struct, and four distinct ways of declaring the variables.
 
R

Robbie Brown

Robbie Brown said:
On 2/3/2014 11:59 AM, Robbie Brown wrote:
[...]
So now I appear to have 4 different ways to declare/define a struct.
I wonder how many more there.

I'm not sure what your "4 different ways" are.

I have four struct declarations in my 'learning about structs' file

typedef struct Foobar{
int a;
int b;
}Foobar, *fbar;

struct Barbaz{
int a;
int b;
}Barbaz, *bbaz;

typedef struct{
long x;
long y;
}AArdvark;

struct{
char *cp1;
char *cp2;
}Whoami;

Those are not 4 different ways *of doing the same thing*.

True but there are four of them and they are different, they have
different forms, contain different numbers of characters and have
different semantics. My original statement was.

"So now I appear to have 4 different ways to declare/define a struct.
I wonder how many more there are"

I don't think I was suggesting they were four different ways of doing
the *same* thing unless 'same thing' means declare a(n arbitrary) struct.
The first defines a struct type, an alias for the struct type, and an
alias for the corresponding pointer-to-struct type; it defines no
objects.
Understood.

The second defines a struct type, an object of the struct type, and an
object of the corresponding pointer-to-struct type.
Understood.

The third defines an anonymous struct type and an alias for that type
(again, it defines no objects).
Understood

The fourth defines an object of an anonymous struct type.

Understood. Thanks for the clear and concise explanation. Are there any
other ways?
The unqualified word "struct" or "structure" is commonly used either to
refer to a struct type or to an object of some struct type. For
clarity, I suggest avoiding using the word "struct" or "structure" by
itself, instead using it as an adjective: "struct type", "struct object"
(also "struct expression", an expression of struct type, and "struct
value", a value of some struct type). Among other benefits, that would
probably have avoided the confusion of thinking of those 4 snippets as
"4 different ways to declare/define a struct".

But they are different aren't they, you yourself explain the differences
above. If they are not different then they are the same ... but they're
not are they?

If they are not four different ways to declare/define a struct and if
they are not four different ways to do the same thing then what are they?

Really, I'm not trying to be awkward. What are those four different
'things' if not ways to declare/define a struct?
 
E

Eric Sosman

[...]
If they are not four different ways to declare/define a struct and if
they are not four different ways to do the same thing then what are they?

Really, I'm not trying to be awkward. What are those four different
'things' if not ways to declare/define a struct?

Let's step *w-a-a-y* back. Consider:

int x;
int *xx;
int xxx[42];
typedef int xxxx;

Four declarations, all different: One declares an `int' variable,
one an `int' pointer, one an array of `int', and the last an
alias for `int'.

But `int' is exactly the same in all four. True, one doesn't
"declare" the `int' type (its declaration is built-in to the C
compiler and/or language), so the analogy is imperfect. But the
point Keith and I are trying to make remains the same: What you
do with a type once it's declared (or built in) doesn't make any
difference to the declaration (or to the built-in). The "four
ways" you speak of are just four uses of the struct type, after
it's declared, not four different ways of declaring a type.
 
R

Robbie Brown

On 2/3/2014 11:59 AM, Robbie Brown wrote:
[...]
So now I appear to have 4 different ways to declare/define a struct.
I wonder how many more there.

I'm not sure what your "4 different ways" are.

I have four struct declarations in my 'learning about structs' file
snip

... but all of these are just uses of the type, not declarations
of it.

Thanks for the great explanation
I get the impression that you're floundering.


Not at all, I'm having enormous fun, making mistakes and learning.
 
J

James Kuyper

On 02/03/2014 03:20 PM, BartC wrote:
....
Four ways of creating a 'struct type' though. The following all do the same
thing: defining a variable d1, d2 etc which is a struct of 3 integers (I've
had to give each name a different numeric suffix just so I could compile
them all together):

True, but the fact that there are several different ways to do that is
because the different ways differ with regard to things that they do in
addition to defining those variables.
struct {int day,month,year;} d0;

This only defines the variables.
struct date1 {int day,month,year;} d1;

This defines the variables AND defines a struct tag for their type.
struct date2 {int day,month,year;};

This defines a struct tag for a type.
struct date2 d2;

This defines variables of a previously defined struct type identified by
it's tag.
typedef struct {int day,month,year;} Date3;

This defines the variables AND a typedef for their type.
Date3 d3;

This defines variables of a previously defined struct type identified by
it's typedef.
typedef struct date4 {int day,month,year;} Date4;

This defines a struct tag and a typedef name for the same type.

If you don't want to do multiple things at the same time, you don't have to.

This is, in principle, no different from the fact that there's several
different ways to add two numbers, depending upon what else happens in
the same expression: a++ + b++ is quite different from a+b, which in
turn is quite different from a-- + b--, but they all add the current
values of a and b.
 
B

BartC

James Kuyper said:
On 02/03/2014 03:20 PM, BartC wrote:
...

True, but the fact that there are several different ways to do that is
because the different ways differ with regard to things that they do in
addition to defining those variables.

Maybe, but sometimes you do just want to declare a variable of a struct
type, but there are all these possibilies with their subtle differences.

The way I'm used to defining variables of type T is:

T a, b, c;

but with structs, then T might be 'struct Tag' (defined elsewhere), or
'struct Tag {...} (defined here but useable elsewhere) or struct {...}
(defined and only useable here) or it might just be a typedef (defined
elsewhere). These forms are used even when you don't need or want all those
other things!

Some type constructors (pointer-to, array-of) are useful to define in-situ,
rather than have to be formally typedef-ed; and any such constructor will be
compatible with any identical constructor elsewhere.

But structs are different! I believe in formally defining structs separately
and in giving them a name via a typedef, then just using that name:

T a, b, c;

(Structs and unions appearing inside another struct or union are different.
Although they use the same reserved words, I think of them as having a
somewhere different function (grouping and controlling the layout of
members). Especially when they are anonymous (not specifying a member name
for the sub-struct or -union).)
This is, in principle, no different from the fact that there's several
different ways to add two numbers, depending upon what else happens in
the same expression: a++ + b++ is quite different from a+b, which in
turn is quite different from a-- + b--, but they all add the current
values of a and b.

These are fairly univeral. But C's treatment of structs isn't.
 
J

James Kuyper

Maybe, but sometimes you do just want to declare a variable of a struct
type, but there are all these possibilies with their subtle differences.

The way I'm used to defining variables of type T is:

T a, b, c;

but with structs, then T might be 'struct Tag' (defined elsewhere), or
'struct Tag {...} (defined here but useable elsewhere) or struct {...}
(defined and only useable here) or it might just be a typedef (defined
elsewhere). ...

The possibility of using a typedef exists with any type, it's not
exclusive to structs.
... These forms are used even when you don't need or want all those
other things!

You don't have to declare a struct tag unless you need it; the same is
true of a typedef, and you're generally free to choose one or the other.
You never have to define an object of a given type in the same
declaration where you define the type - they can always be separated.
Therefore, I'm not quite sure what you're referring to. You do need to
understand such constructs if they are written by someone else, but
that's a different issue.

You do have to define the struct type somewhere (unless it's one of the
structs defined in the standard library), which is inherently different
from, for example, "int". But that is inherent in the fact that it is a
user defined type.
These are fairly universal. ...

Except for function call expressions, C's expressions with both values
and side effects (a=b, a++, a+=b, etc) are far from universal; I've used
many languages with no equivalent to any of them: assignment is a
statement in those languages, not an expression with a value that can be
used as an operand in another expression. I believe that most of the
languages which do support them were at least partly inspired by C.
Without such expressions, it's difficult to construct multiple
meaningfully different C expressions that calculate a+b.
 
K

Keith Thompson

BartC said:
Not really. C has all these extra aspects to defining struct types that
other languages that have records don't have and don't appear to need. And
with so many extra possibilities, together with the concept of 'tag' names
(another new idea) having their own namespace, it can get confusing for
beginners (for others too!). Trying to explain it doesn't make it any
simpler, or code that mixes all these up less confusing.

So I try to:

- Always use typedefs to wrap struct types

- Not use struct tags

- Not use anonymous structs

(But with some exceptions)

Not using struct tags in all cases is not an option if you want to have
a struct containing a pointer to the same type.

You can *effectively* avoid struct tags by giving them names that you
won't be tempted to use. For example:

typedef struct node_s node;
struct node_s {
void *data;
node *prev;
node *next;
};

Or, more simply:

typedef struct node_s {
void *data;
struct node_s *prev;
struct node_s *next;
} node;

Or the above with "node_s" replaced by "node".

(It's not the way I'd do it, but I've said that enough times
that I won't repeat the details here.)
 
K

Keith Thompson

Robbie Brown said:
On 03/02/14 20:04, Keith Thompson wrote: [...]
The first defines a struct type, an alias for the struct type, and an
alias for the corresponding pointer-to-struct type; it defines no
objects.
Understood.

The second defines a struct type, an object of the struct type, and an
object of the corresponding pointer-to-struct type.
Understood.

The third defines an anonymous struct type and an alias for that type
(again, it defines no objects).
Understood

The fourth defines an object of an anonymous struct type.

Understood. Thanks for the clear and concise explanation. Are there any
other ways?

Are there any other ways *to do what*?

We've seen code that defines a struct type (with or without a tag),
defines an alias for a struct type, defines an alias for a
pointer-to-struct type, defines an object of a struct type, and defines
an object of a pointer-to-struct type. Would defining an object of a
pointer-to-pointer-to-struct type qualify as "another way"?

It's particularly difficult to tell what you're asking if you use the
word "struct" as a noun; it ignores the distinction between a struct
*type* and a struct *object*.

There are at least as many different ways to do things as there are
different things to do.
 
J

Jorgen Grahn

Not really. C has all these extra aspects to defining struct types that
other languages that have records don't have and don't appear to need. And
with so many extra possibilities, together with the concept of 'tag' names
(another new idea) having their own namespace, it can get confusing for
beginners (for others too!). Trying to explain it doesn't make it any
simpler, or code that mixes all these up less confusing.

So I try to:

- Always use typedefs to wrap struct types

- Not use struct tags

- Not use anonymous structs

(But with some exceptions)

I haven't followed the thread closely, but one data point ... I do it
the other way around: never use typedefs, and always use struct tags.
I won't claim this is the dominant style, but I'm not alone, based on
earlier discussions here.

One reason I dislike typedefed structs is that (as far as I can tell)
you cannot forward-declare them.

/Jorgen
 
J

James Kuyper

On 02/05/2014 12:47 PM, Jorgen Grahn wrote:
....
One reason I dislike typedefed structs is that (as far as I can tell)
you cannot forward-declare them.

typedef struct tag name;

name *pname;

struct tag{
int i;
double d;
};

name object;

That last line would be a constraint violation if moved before the
struct tag definition.
 
K

Keith Thompson

James Kuyper said:
On 02/05/2014 12:47 PM, Jorgen Grahn wrote:
...

typedef struct tag name;

name *pname;

struct tag{
int i;
double d;
};

name object;

Yes, but you can do that only if you use struct tags as well as
typedefs. (I'm not saying that's a bad idea, but it's inconsistent with
what BartC said upthread about avoiding struct tags.)
 
J

James Kuyper

Yes, but you can do that only if you use struct tags as well as
typedefs.

Sorry - I missed that aspect of what Jorgen was saying. He's right -
typedefs of tagless structs cannot be forward declared.
 
J

Jorgen Grahn

Sorry - I missed that aspect of what Jorgen was saying. He's right -
typedefs of tagless structs cannot be forward declared.

For once I would have been happy to be proven wrong ...

What I often want to do in practice is to deal with 'struct foo*'
from some library, without having to pull in the library's header file.
I like every translation unit to know as little as possible -- see as
few names as possible.

/Jorgen
 

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

Forum statistics

Threads
474,075
Messages
2,570,553
Members
47,197
Latest member
NDTShavonn

Latest Threads

Top