structure and union queries

T

Tagore

hi,
Please consider 2 programs below and queries assoicated with them:
PROGRAM-1:
#include<stdio.h>
typedef union
{
float f1;
float f2;
int i;
}sample;
int main()
{
sample S={66};
printf("%f %f %d",S.f1,S.f2,S.i);
return 0;
}

OUTPUT:
66.000000 66.000000 1115947008
I feel myself unable to understand above output...I expected that as
we are initializing S with 66 ,a integer, so It should print first two
grabage value when printing floating values.and then print 66 but I
found it otherwise here.

PROGRAM-2:
#include<stdio.h>
typedef struct sample
{
int i;
char a[10];
}data;
int main()
{
printf("%d", sizeof(data.a));
return 0;
}
OUTPUT:
In function 'main':
Line 10: error: expected ')' before '.' token
I expected output of this function to be 10 which is size of array
a....but could not understand this error.

Please help me regarding both queries
 
Z

Ze Long

Tagore 写é“:
hi,
Please consider 2 programs below and queries assoicated with them:
PROGRAM-1:
#include<stdio.h>
typedef union
{
float f1;
float f2;
int i;
}sample;
int main()
{
sample S={66};
printf("%f %f %d",S.f1,S.f2,S.i);
return 0;
}

OUTPUT:
66.000000 66.000000 1115947008
I feel myself unable to understand above output...I expected that as
we are initializing S with 66 ,a integer, so It should print first two
grabage value when printing floating values.and then print 66 but I
found it otherwise here.

PROGRAM-2:
#include<stdio.h>
typedef struct sample
{
int i;
char a[10];
}data;
int main()
{
printf("%d", sizeof(data.a));
return 0;
}
OUTPUT:
In function 'main':
Line 10: error: expected ')' before '.' token
I expected output of this function to be 10 which is size of array
a....but could not understand this error.

Please help me regarding both queries

I don't know how does union work. But for the second program, you are
defining a type named data, not a structure. The right way to do this is
struct sample
{
int i;
char a[10];
}data;

Notice there is not "typedef" in the first line.
 
J

jameskuyper

Tagore said:
hi,
Please consider 2 programs below and queries assoicated with them:
PROGRAM-1:
#include<stdio.h>
typedef union
{
float f1;
float f2;
int i;
}sample;
int main()
{
sample S={66};

This code initializes S.f1 with a value of 66.0.
printf("%f %f %d",S.f1,S.f2,S.i);

Any attempt to use the value of S.f2 or s.i at this point in the
program has undefined behavior. The only member of a union that you
can safely read is the member that was last written to.
return 0;
}

OUTPUT:
66.000000 66.000000 1115947008
I feel myself unable to understand above output...I expected that as
we are initializing S with 66 ,a integer, so It should print first two
grabage value when printing floating values.and then print 66 but I
found it otherwise here.

The rule for initializing unions is that it's always the first member
of the union that gets initialized, unless you specify otherwise (the
ability to specify a different member is a C99 feature); the type of
the initializer is irrelevant. The initializer is implicitly converted
to the type of that member, as-if by assignment. In other words, your
code is equivalent to

sample S;
S.f1 = 66;

If you want to intialize i at the same time you define S, you can't do
it in C90. In C99 you can write:
sample S = {.i=66};
PROGRAM-2:
#include<stdio.h>
typedef struct sample
{
int i;
char a[10];
}data;
int main()
{
printf("%d", sizeof(data.a));

This is the wrong format specifier to use. %d is for 'int'. The value
of a sizeof expression has the type size_t, which is an unsigned
integer type, and often one that is incompatible with 'int'. As a
result, your printf() call has undefined behavior. See below for two
different options for dealing with this problem.
return 0;
}
OUTPUT:
In function 'main':
Line 10: error: expected ')' before '.' token
I expected output of this function to be 10 which is size of array
a....but could not understand this error.

You've defined 'data' to be a typedef for struct sample. You can write
"sizeof(type)" or "sizeof expression", but data.a is neither a type
nor an expression, it's a constraint violation. The left operand of
the '.' operator must be an expression which has a struct or union
type. 'data' is a type, not an expression. It IS a struct type; it is
not an expression which has struct type.

What you can do is write an expression whose type is the type of 'a':

/* Use this form only for C90; unsigned long might not be big
enough in C99. */
printf("%lu", (unsigned long)sizeof ((data *)NULL)->a);

/* Use this form only for C99; %z is not supported in C90. */
printf("%zu", sizeof ((data *)NULL)->a);

Those expressions look like they dereference null pointers, but the
operand of a sizeof expression is not evaluated, (except in C99, and
even then, only if the expression involves a VLA). The only thing
"sizeof expression" does is determine what the type of the expression
would be, if it were evaluated, so those expressions are perfectly
safe.
 
J

Jens Thoms Toerring

Tagore said:
Please consider 2 programs below and queries assoicated with them:
PROGRAM-1:
#include<stdio.h>
typedef union
{
float f1;
float f2;
int i;
}sample;
int main()
{
sample S={66};
printf("%f %f %d",S.f1,S.f2,S.i);
return 0;
}
OUTPUT:
66.000000 66.000000 1115947008
I feel myself unable to understand above output...I expected that as
we are initializing S with 66 ,a integer, so It should print first two
grabage value when printing floating values.and then print 66 but I
found it otherwise here.

No, just because there's an int in the union and the value you use
for the initializaton is an int, the int member of the union isn't
initialized automatically. To cite C standard: "A brace-enclosed
initializer for a union object initializes the member that appears
first in the declaration list of the union type." That means that
in your case the 'f1' member of the union gets initialized (after
converting 66 to a float value).

Please also note that taking the value of the element of an union
that wasn't the last one to be assigned a value to results in
undefined behaviour - rather likely not a problem here with this
test program but better avoided in a real program.
PROGRAM-2:
#include<stdio.h>
typedef struct sample
{
int i;
char a[10];
}data;
int main()
{
printf("%d", sizeof(data.a));
return 0;
}
OUTPUT:
In function 'main':
Line 10: error: expected ')' before '.' token
I expected output of this function to be 10 which is size of array
a....but could not understand this error.

You can only apply 'sizeof' to either a type or a variable.
Here 'data' would be a type, but 'data.a' isn't. On the other
hand, if you would create an instance of the structure, like

data mydata;

you then could do

printf( "%lu", ( unsigned long ) sizeof mydata.a q);

Please note the cast of 'sizeof' and the use of "%lu" - what
'sizeof' results in may not be (and often isn't) an int and
lying to printf() (or other variadic functions) is never a
good idea...
Regards, Jens
 
K

Keith Thompson

Tagore said:
Please consider 2 programs below and queries assoicated with them:
PROGRAM-1:
#include<stdio.h>
typedef union
{
float f1;
float f2;
int i;
}sample;
int main()
{
sample S={66};
printf("%f %f %d",S.f1,S.f2,S.i);
return 0;
}

OUTPUT:
66.000000 66.000000 1115947008
I feel myself unable to understand above output...I expected that as
we are initializing S with 66 ,a integer, so It should print first two
grabage value when printing floating values.and then print 66 but I
found it otherwise here.

The initializer initializes the *first* member of the union (not the
first one that happens to match the expression's type). In this case,
the int value 66 is converted to float, so S.f1 is 66.0. S.f2, since
it's of the same type and occupies the same location in memory, also
has the value 66.0. S.i has whatever int value corresponds to the
float representation of 66.0 (in this case, apparently that's
1115947008, or 0x42840000).
PROGRAM-2:
#include<stdio.h>
typedef struct sample
{
int i;
char a[10];
}data;
int main()
{
printf("%d", sizeof(data.a));
return 0;
}
OUTPUT:
In function 'main':
Line 10: error: expected ')' before '.' token
I expected output of this function to be 10 which is size of array
a....but could not understand this error.

"data" is a type. You can't legally refer to data.a. You have to
have an object or expression of type data; then you can refer to a
member of that object or expression.

To take a simpler example:

typedef struct {
int i;
} my_type;

my_type my_object;

"my_type.i" is invalid, even as an argument to sizeof. "my_object.i"
is valid; it refers to the i member of the object "my_object".

(If "sizeof(my_type.i)" were legal, presumably it would give you the
size of the i member of the type my_type. But it isn't, so it
doesn't.)
 
K

Keith Thompson

You can only apply 'sizeof' to either a type or a variable.

Quibble: you can apply 'sizeof' either to a type (which must be in
parentheses) or to an expression (specifically a unary-expression).
For example, ``sizeof 42'' is legal, though 42 is neither a type nor a
variable.
Here 'data' would be a type, but 'data.a' isn't.
[...]

The problem is that ``data.a'' is illegal, since the left operand of
"." must be an expression, and data is a type name. The fact that
it's being used as the operand of "sizeof" doesn't enter into it;
``data.a'' is illegal in any context.
 
L

lawrence.jones

jameskuyper said:
Any attempt to use the value of S.f2 or s.i at this point in the
program has undefined behavior. The only member of a union that you
can safely read is the member that was last written to.

That was the rule in C90, but the rules in C99 recognize what really
happens -- the existing bits are reinterpreted in the other type. Since
f2 has the same type as the member that was last written to, the
behavior of accessing it is completely defined. Accessing i is a bit
more fluid; since the types are different, the existing bits may or may
not be a valid representation. If they are, the behavior is well
defined but the resulting value is unspecified. If not, all bets are
off and you still get undefined behavior.
 
F

Flash Gordon

Joe said:
I still don't get it.

union s {
float f;
int i;
};

union s S;
S.f = 66;

The union S is essentially a 32-bit object with two names.

On your implementation, possibly.
The above
assignment will set the bits of S like this..

01000010 10000100 00000000 00000000

If I then printf("%d\n", S.i) I will get..

1115947008

What if int is 64 bits but float only 32? What if int has a trap
representation (as allowed by the standard) and you pick a float that
happens to be represented by that particular bit pattern?
and then printf("%f\n", S.f) I will get..

66.000000

I read S.i first, after assigning to S.f. Assuming float and int are
both 32 bits, what about the foregoing is undefined behavior? What can
possibly be wrong with it?

You happen to use a float value that produces a bit pattern that is not
a valid representation for an int as Lawrence said, i.e it is a trap
representation. If there are no trap representations on your
implementation then you won't get undefined behaviour.
 
J

jameskuyper

Joe said:
(e-mail address removed) wrote: ....

I still don't get it.

union s {
float f;
int i;
};

union s S;
S.f = 66;

The union S is essentially a 32-bit object with two names. The above
assignment will set the bits of S like this..

01000010 10000100 00000000 00000000

If I then printf("%d\n", S.i) I will get..

1115947008

and then printf("%f\n", S.f) I will get..

66.000000

I read S.i first, after assigning to S.f. Assuming float and int are
both 32 bits, what about the foregoing is undefined behavior?

The fact that float and int are two different types.
What can
possibly be wrong with it?

The C standard merely allows it to go wrong, because there are real
implementations where it can go wrong. Potential problems include the
possibility that a valid value for an 'int' might be represented by a
bit pattern which represents an invalid value for a float (or vice
versa). The C standard calls these bit patterns "trap
representations", and any attempt to use an lvalue of a given type to
retrieve the value of a piece of memory that contains a trap
representation for that type has undefined behavior. There are real
implementations where either 'int' or 'float' has trap
representations, and where attempting to load an invalid bit pattern
will produce seriously nasty consequences.

Even on platforms without such exotic hardware, the fact that the
standard says that this has undefined behavior indirectly opens up
other possible failure modes. For instance, it gives implementations
permission to perform optimizations that would otherwise be illegal.
Example:

S.f = 66.0F;
// Compiler leaves a copy of 66.0 in a floating point register.
S.i = -3;
printf("%f\n", S.f)

Since the behavior is undefined if you attempt to read S.f when the
most recent write was to S.i, the compiler can perform an optimization
based upon the assumption that you have NOT performed such a write
(even though it's perfectly obvious that you have - the compiler is
not required to pay attention to such things when the behavior is
undefined). Specifically, it sees that the last time a value was
written to 'S.f', that value was stored in a specific register, and
that register has not been used for any other purpose since then.
Since there is no code with defined behavior that could have changed
the value of 'f' since that time, it can legally pass the value of
66.0 that was stored in the floating point register to printf(). In
other words, it's not required to notice that S.i occupies the same
piece of memory as S.f when making this decision.

This is a simple case, where such an "optimization" might seem
perverse. However, it might significantly simplify the design of the
compiler code that performs this optimization, if it doesn't have to
bother checking for possibilities that have undefined behavior. As
long as the optimization works correctly whenever the behavior is
defined, such an optimization does not prevent a compiler from being
conforming.

This is an a example of a more general rule: whenever the C standard
says that a given piece of code has undefined behavior, it gives
implementations permission to simplify their compilers by not
bothering to handle such code in the way you might naively think was
correct. As a result, the simple fact that code has undefined behavior
opens up failure modes in addition to the ones that were the original
reason the committee decided to make the behavior undefined.
 
C

CBFalconer

Joe said:
.... snip ...

I still don't get it.

union s {
float f;
int i;
};

union s S;
S.f = 66;

The union S is essentially a 32-bit object with two names. The
above assignment will set the bits of S like this..

01000010 10000100 00000000 00000000

If I then printf("%d\n", S.i) I will get..

1115947008

and then printf("%f\n", S.f) I will get..

66.000000

I read S.i first, after assigning to S.f. Assuming float and int
are both 32 bits, what about the foregoing is undefined behavior?
What can possibly be wrong with it?

The 'wrong' thing is some idiot assuming that the access to S.f
magically reorganizes the bits in S to form the integer 66. There
are more and more of these idiots around, who have no idea what
really goes on, and who depend on the use of 'SHAZAM'.
 
B

Barry Schwarz

I still don't get it.

union s {
float f;
int i;
};

union s S;
S.f = 66;

The union S is essentially a 32-bit object with two names. The above
assignment will set the bits of S like this..

01000010 10000100 00000000 00000000

On my system the bits will be
01000010 01100110 00000000 00000000
My system uses hex floating point, not binary, so the leading bit in a
normalized mantissa need not be 1.
If I then printf("%d\n", S.i) I will get..

1115947008

You appear to have a big endian system. What abut the poor people
with little endian ones? Will they see 33860?

I would probably get 1113980928 but that still doesn't alter the fact
that the behavior is undefined.
and then printf("%f\n", S.f) I will get..

66.000000

I read S.i first, after assigning to S.f. Assuming float and int are
both 32 bits, what about the foregoing is undefined behavior? What can
possibly be wrong with it?

The fact that the C90 standard says it is undefined. The compiler is
not even obligated to generate the "expected" code, even though almost
all do.

It is not much different than passing overlapping fields to strcpy or
memcpy. On most systems it will do the "expected." It is still
undefined.
 
G

Guest

Joe Wright wrote:

[stuff to do with unions]


the representations of int and float are different.
and assuming both are 32-bits is a big assumption.

Signalling NaN

The 'wrong' thing is some idiot assuming that the access to S.f
magically reorganizes the bits in S to form the integer 66.  There
are more and more of these idiots around, who have no idea what
really goes on, and who depend on the use of 'SHAZAM'.

A bit unfair, I think. Floating point has some non-intuitive
properties
and with unions you are getting pretty chummy with the hardware.

At least this guy is asking the questions.
 
K

Keith Thompson

Joe Wright wrote: [...]
   union s {
     float f;
     int i;
   };
   union s S;
   S.f = 66; [...]
What can possibly be wrong with it?

Signalling NaN
[...]

Signalling NaNs aren't an issue in this particular case. The value 66
(converted to 66.0f) was stored in S.f. The problem is that S.i could
contain a trap representation, either because the bit pattern used to
represent 66.0f happens to be a trap representation for type int, or
because int is bigger than float and the remaining bits of S.i are
garbage.

(In practice, most implementations don't have trap representations for
type int, but you can't assume that in portable code.)
 
J

James Kuyper

CBFalconer said:
Joe Wright wrote:
... snip ...

The 'wrong' thing is some idiot assuming that the access to S.f
magically reorganizes the bits in S to form the integer 66. There

He's said nothing to suggest that he's expecting a magical
reorganization. In one of the earlier messages which are invisible to
you, he has very explicitly stated that he expects the member with the
type that is different from the one most recently written to have a
"garbage" value, by which he apparently means a value which has a
relationship to the value written that can only be predicted by a
complicated process that depends upon the precise details of how both
'int' and 'float' are represented, and as such is highly non-portable.

What he didn't understand was how the consequences could be anything
worse than a garbage value. He was apparently unfamiliar with the
possibility of trap representations, and couldn't imagine any of the
more exotic ways in which the code could fail.
 
L

lawrence.jones

jameskuyper said:
Even on platforms without such exotic hardware, the fact that the
standard says that this has undefined behavior indirectly opens up
other possible failure modes. For instance, it gives implementations
permission to perform optimizations that would otherwise be illegal.

Not in C99.
 
C

CBFalconer

CBFalconer said:
Joe Wright wrote:
[stuff to do with unions]
.... snip ...
The 'wrong' thing is some idiot assuming that the access to S.f
magically reorganizes the bits in S to form the integer 66. There
are more and more of these idiots around, who have no idea what
really goes on, and who depend on the use of 'SHAZAM'.

A bit unfair, I think. Floating point has some non-intuitive
properties and with unions you are getting pretty chummy with the
hardware. At least this guy is asking the questions.

You have a point. However, to me, the fact that two items occupy
the same space and can represent different sets of values screams
that "the format is different".
 
J

James Kuyper

CBFalconer said:
CBFalconer said:
Joe Wright wrote:
[stuff to do with unions]
union s {
float f;
int i;
};

union s S;
S.f = 66;

The union S is essentially a 32-bit object with two names. The
above assignment will set the bits of S like this..

01000010 10000100 00000000 00000000

If I then printf("%d\n", S.i) I will get..

1115947008

and then printf("%f\n", S.f) I will get..

66.000000

I read S.i first, after assigning to S.f. Assuming float and int
are both 32 bits, what about the foregoing is undefined behavior? ... snip ...
The 'wrong' thing is some idiot assuming that the access to S.f
magically reorganizes the bits in S to form the integer 66. There
are more and more of these idiots around, who have no idea what
really goes on, and who depend on the use of 'SHAZAM'.
A bit unfair, I think. Floating point has some non-intuitive
properties and with unions you are getting pretty chummy with the
hardware. At least this guy is asking the questions.

You have a point. However, to me, the fact that two items occupy
the same space and can represent different sets of values screams
that "the format is different".

Yes, but he said nothing to suggest that he was unaware of that fact. He
in no way implied that he expected anything like the magical
reorganization you refer to. He just expected that the bits that were
written as a floating point value would be reinterpreted as a
representing a very different integer value, with no unexpected side
effects.
 
C

CBFalconer

James said:
CBFalconer said:
Joe Wright wrote:

[stuff to do with unions]

union s {
float f;
int i;
};

union s S;
S.f = 66;

The union S is essentially a 32-bit object with two names. The
above assignment will set the bits of S like this..

01000010 10000100 00000000 00000000

If I then printf("%d\n", S.i) I will get..

1115947008

and then printf("%f\n", S.f) I will get..

66.000000

I read S.i first, after assigning to S.f. Assuming float and int
are both 32 bits, what about the foregoing is undefined behavior?

... snip ...
The 'wrong' thing is some idiot assuming that the access to S.f
magically reorganizes the bits in S to form the integer 66.
There are more and more of these idiots around, who have no
idea what really goes on, and who depend on the use of 'SHAZAM'.

A bit unfair, I think. Floating point has some non-intuitive
properties and with unions you are getting pretty chummy with
the hardware. At least this guy is asking the questions.

You have a point. However, to me, the fact that two items occupy
the same space and can represent different sets of values screams
that "the format is different".

Yes, but he said nothing to suggest that he was unaware of that
fact. He in no way implied that he expected anything like the
magical reorganization you refer to. He just expected that the
bits that were written as a floating point value would be
reinterpreted as a representing a very different integer value,
with no unexpected side effects.

Any such would require storage of the actual stored format, and the
requested format. This can be an unlimited number of
possibilities. Very minor thinking can infer the magic
requirement, IMO. Since all objects are typed, casts provide that
sort of information.
 
J

James Kuyper

CBFalconer said:
James said:
CBFalconer said:
(e-mail address removed) wrote:
Joe Wright wrote:

[stuff to do with unions]

union s {
float f;
int i;
};

union s S;
S.f = 66;

The union S is essentially a 32-bit object with two names. The
above assignment will set the bits of S like this..

01000010 10000100 00000000 00000000

If I then printf("%d\n", S.i) I will get..

1115947008

and then printf("%f\n", S.f) I will get..

66.000000

I read S.i first, after assigning to S.f. Assuming float and int
are both 32 bits, what about the foregoing is undefined behavior?
... snip ...

The 'wrong' thing is some idiot assuming that the access to S.f
magically reorganizes the bits in S to form the integer 66.
There are more and more of these idiots around, who have no
idea what really goes on, and who depend on the use of 'SHAZAM'.
A bit unfair, I think. Floating point has some non-intuitive
properties and with unions you are getting pretty chummy with
the hardware. At least this guy is asking the questions.
You have a point. However, to me, the fact that two items occupy
the same space and can represent different sets of values screams
that "the format is different".
Yes, but he said nothing to suggest that he was unaware of that
fact. He in no way implied that he expected anything like the
magical reorganization you refer to. He just expected that the
bits that were written as a floating point value would be
reinterpreted as a representing a very different integer value,
with no unexpected side effects.

Any such would require storage of the actual stored format, and the
requested format.

As the result depends only upon the bits that were stored, and not upon
the format that was used to store them, why would you need to store that
format?

The requested format is determined by the union member used to refer to
the object, there is no need to store this information anywhere, except
in the sense that the sequence of instructions in the executable program
that perform the retrieval can be considered as storing information
about which format that is.
... This can be an unlimited number of
possibilities. ...

No, in a union with two members, there's only two possibilities; only
the one that corresponds to the member you are currently referring to
actually matters at any given point.
... Very minor thinking can infer the magic
requirement, IMO.

What's magical about writing the contents of a floating point register
to RAM, then reading the contents of that ram into an integer register?
I get the impression that you are still of the mistaken opinion that he
expressed an expectation that the integer he retrieved would have the
same numerical values as the floating point number he stored. He
expressed no such expectation.
... Since all objects are typed, ...

That is not true. Dynamically allocated memory constitutes an object
that has no declared type, and no effective type. It can acquire an
effective type by being written to using an lvalue that is not of
character type, or by a memcpy() from another object that does have an
effective type. But until that happens, that object does not have a type
(6.5p6).
 

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

Similar Threads

union, strcpy and main() 10
Union and strict aliasing 4
union 7
union 16
[union] Pointers to inherited structs are valid ? 104
Union trouble 7
union member 8
C doubt- union 5

Members online

Forum statistics

Threads
473,763
Messages
2,569,562
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top