union access

S

Sean Dolan

typedef struct ntt {
int type;
union {
int i;
char* s;
};
}nt;

nt n;
n.i = 0;

I found a C example like this and could not get gcc 2.95.4 to compile it
(struct has no member named `i') until I declared an instance of the union:
union {
int i;
char* s;
}u;

and accessed like:
n.u.i = 0;

Is the first example valid? If so, where is the problem?
 
D

Derrick Coetzee

Sean said:
typedef struct ntt {
int type;
union {
int i;
char* s;
};
}nt;

This is called an anonymous union and it is a Microsoft extension. While
gcc supports MS extensions with the flag -fms-extensions, you'd do
better converting it to standard C. You can do this as you suggested. If
you want to avoid fixing up all references, you can simply dispose of
the union, costing an extra word or so per object of this type - which
may or may not be a big deal in your app. If the names inside the union
are globally unique, you can use macros to expand them out to their
fully-qualified versions.
 
S

Sean Dolan

This is called an anonymous union and it is a Microsoft extension. While
gcc supports MS extensions with the flag -fms-extensions, you'd do
better converting it to standard C. You can do this as you suggested. If
you want to avoid fixing up all references, you can simply dispose of
the union, costing an extra word or so per object of this type - which
may or may not be a big deal in your app. If the names inside the union
are globally unique, you can use macros to expand them out to their
fully-qualified versions.

Thank you Derrick, that cleared it right up.
 
K

Keith Thompson

Sean Dolan said:
typedef struct ntt {
int type;
union {
int i;
char* s;
};
}nt;

nt n;
n.i = 0;

I found a C example like this and could not get gcc 2.95.4 to compile it
(struct has no member named `i') until I declared an instance of the union:
union {
int i;
char* s;
}u;

and accessed like:
n.u.i = 0;

Is the first example valid? If so, where is the problem?

No, the first example is not valid. In the member declaration

int type;

"int" is the member type and "type" is the name of the member.

In the (attempted) member declaration

union { int i; char *s; };

"union { int i; char *s; }" is the member type, but there is no member
name. (Sub-unions and sub-structs don't flatten into the namespace of
the surrounding struct or union.)
 
C

c453___

one of simple/silly questions:
why use unions when we have classes? what makes union worth of using?
 
K

Keith Thompson

c453___ said:
one of simple/silly questions:
why use unions when we have classes? what makes union worth of using?

Because we don't have classes. (This is comp.lang.c, not comp.lang.c++.)

And, of course, unions and classes are two different things. The
point of a union is that the members are all stored in the same
location (and reading a member other than the last one written invokes
undefined behavior in most cases).
 
C

Christian Bau

c453___ <[email protected]> said:
one of simple/silly questions:
why use unions when we have classes? what makes union worth of using?

Unions are very useful to organise employees to negotiate higher
salaries, better working conditions etc. Classes are more useful to
learn stuff, like C classes where someone teaches C and people like you
should listen carefully to learn.

There should be Usenet classes as well, where people could learn not to
post C++ questions to comp.lang.c.
 
X

xarax

c453___ said:
;) why use unions if we have structures?

All members of a union start at offset zero.

Each successive member of a struct starts at the next
available offset that is appropriate for the alignment
of that member and the preceding (if any) member.
 
C

Chris Barts

;) why use unions if we have structures?

To save space, mainly. With a union, each member overlaps in memory, so
you can have one spot to store objects of different types. Of course,
accessing an object of a different type than was last stored (for example,
reading a double after you've just stored an int) is nonconforming, highly
unportable, and somewhat dangerous.

I have never actually used them in any of my programs, but I suppose I'm
not a typical programmer.
 
M

Mark Piffer

Keith Thompson said:
And, of course, unions and classes are two different things. The
point of a union is that the members are all stored in the same
location (and reading a member other than the last one written invokes
undefined behavior in most cases).

Regarding unions and their usefulness, can anyone point out UB in the
following code? It tries to stack up "local" variables in an array of
char intended to be used by a call hierarchy of statemachines. To save
memory, only the actually needed local variables are allocated,
although the enclosing union is larger. How bad is this with regard to
the standard?


#include <stdio.h>
#include <stdint.h>

enum fsm_state { IDLE, LOAD, STORE, SEARCH };
struct fsm_common{ enum fsm_state State; };
struct fsm_type1{ enum fsm_state State; int Length; long Addr; };
struct fsm_type2{ enum fsm_state State; long Start; long Stop; long
Addr; };
union fsm_locals{ struct fsm_common common; struct fsm_type1 type1;
struct fsm_type2 type2; };

union fsm_locals* align_to_union(char *ptr) // non-portable
{
uintptr_t a = (void*)ptr; // architecture & implementation dep.
return (void*)((a+3u)&~3u); // choose any alignment appropriate for
the union
}

void* alloc_on_stack(char **StackPtr,size_t s)
{
union fsm_locals* r = align_to_union(*StackPtr);
*StackPtr = (char*)r+s;
return (void*)r;
}

void init_fsm(char *StackPtr)
{
union fsm_locals *locals = align_to_union(StackPtr);
locals->common.State = IDLE;
}

void bar(char *StackPtr)
{
struct fsm_type2 *locals = alloc_on_stack(&StackPtr,sizeof
*locals);
switch(locals->State)
{
case IDLE: locals->State = SEARCH;
break;
case SEARCH: locals->Start = locals->Addr = 0xcccccccc;
locals->Stop = 0xdddddddd;
break;
}
}

void foo(char *StackPtr)
{
struct fsm_type1 *locals = alloc_on_stack(&StackPtr,sizeof
*locals);
switch(locals->State)
{
case IDLE: locals->Length = 0xaaaaaaaa;
locals->Addr = 0xbbbbbbbb;
init_fsm(StackPtr);
locals->State = LOAD;
break;
case LOAD: bar(StackPtr);
break;
}
}

int main(void)
{
unsigned char Stack[2*sizeof(union fsm_locals)];
int i;
init_fsm(Stack);
foo(Stack); foo(Stack); foo(Stack);
for(i=0;i<2*sizeof(union fsm_locals);i++) printf("%.2x " ,
Stack);
return 0;
}


regards,
Mark
 
D

Dan Pop

In said:
union fsm_locals* align_to_union(char *ptr) // non-portable
{
uintptr_t a = (void*)ptr; // architecture & implementation dep.

This is incorrect C code on any architecture & implementation: you can't
assign a pointer to an integer without a cast to integer. A diagnostic is
*required*.
return (void*)((a+3u)&~3u); // choose any alignment appropriate for

This is semantically broken, if uintptr_t is wider than unsigned int.
The correct ANDing mask is computed like this: ~(uintptr_t)3.

I didn't bother reading the rest of the code: you have to master C
*before* attempting this kind of things.

Dan
 
M

Mark Piffer

This is incorrect C code on any architecture & implementation: you can't
assign a pointer to an integer without a cast to integer. A diagnostic is
*required*.
Is it for historic reasons that this kind of assignment isn't being
flagged as syntax error? As far as I understand it, no implicit
conversion acts here, it is just that the UB does the "expected" on
most architectures/compilers.
This is semantically broken, if uintptr_t is wider than unsigned int.
The correct ANDing mask is computed like this: ~(uintptr_t)3.
Yuck. I really slept about this one.
I didn't bother reading the rest of the code: you have to master C
*before* attempting this kind of things.

Dan

If we fancy that I had written the above lines correctly, would you
care then? My questions are more about pointers into unions and their
alignment.

Mark
 
B

Ben Pfaff

Is it for historic reasons that this kind of assignment isn't being
flagged as syntax error?

Trying to make this a syntax error would make the grammar
unnecessarily complex and probably introduce further context
sensitivity. Why do you think it should be a syntax error?
 
K

Keith Thompson

Ben Pfaff said:
Trying to make this a syntax error would make the grammar
unnecessarily complex and probably introduce further context
sensitivity. Why do you think it should be a syntax error?

A lot of people incorrectly use the term "syntax error" to refer to
any error reported by a compiler. The first compiler I used (not a C
compiler) had a list of "syntax errors" that included things like
undeclared identifiers.

I think Mark was asking whether there are historical reasons a
compiler wouldn't issue a diagnostic message for an attempt to assign
a pointer value to an integer object. The answer, I think, is that
earlier (pre-ANSI) versions of C were less strict about type
compatibility.
 
M

Mark Piffer

Keith Thompson said:
A lot of people incorrectly use the term "syntax error" to refer to
any error reported by a compiler. The first compiler I used (not a C
compiler) had a list of "syntax errors" that included things like
undeclared identifiers.
It was a black day for me yesterday. After I had overcome the (valid!)
reprehension from Dan, I went on and produced more rubbish instead of
shutting up.
I think Mark was asking whether there are historical reasons a
compiler wouldn't issue a diagnostic message for an attempt to assign
a pointer value to an integer object. The answer, I think, is that
earlier (pre-ANSI) versions of C were less strict about type
compatibility.
I think I was asking why it is an error trying to assign a scalar type
to a aggregate type and vice versa, but not a pointer to/from an
arithmetic type. It is always incorrect C, so the hierarchy of
mutually assignable types could be restricted to the arithmetic types.

Mark
 
M

Mark Piffer

I think I was asking why it is an error trying to assign a scalar type
to a aggregate type and vice versa, but not a pointer to/from an
arithmetic type. It is always incorrect C, so the hierarchy of
^^^^^^^^^^^^^^^ that should read _integral type_
 
D

Dan Pop

In said:
I think I was asking why it is an error trying to assign a scalar type
to a aggregate type and vice versa, but not a pointer to/from an
arithmetic type.

I don't get you. It *is* an error to assign a pointer to/from an
arithmetic type and the compiler *must* diagnose it. Only *explicit*
conversions (via casts) are allowed between integers and pointers.
It is always incorrect C, so the hierarchy of
mutually assignable types could be restricted to the arithmetic types.

It is already restricted to arithmetic types (with the notable exception
of the null pointer constant and void pointers to/from non-void
non-function pointers).

Are you sure you know what you're talking about?

Dan
 

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
473,777
Messages
2,569,604
Members
45,234
Latest member
SkyeWeems

Latest Threads

Top