Does casting lvalue lead to Undefined Behaviour ?

P

p_cricket_guy

Please see the code below

-- start listing is_it_ub.c --

#include <stdio.h>
#include <stdlib.h>

int main (void)
{
unsigned char buff[20];
unsigned int i;

i = 0xaabbccddUL;
*((int *)buff) = i; /* Is this UB ? */

printf("buff: %x:%x:%x:%x\n", buff[0], buff[1], buff[2], buff[3]);

return EXIT_SUCCESS;
}

-- end listing --

Output:
buff: dd:cc:bb:aa

Output seems correct on my Little Endian PC.

In the statement: "*((int *)buff) = i; ", buff is casted to be treated
as an int *. Is this valid?

My compiler does not produce any diagnostics for the above
program but somebody else complained that their compiler
warns "casting of lvalue is deprecated".

Thanks.
 
G

Guest

Please see the code below

-- start listing is_it_ub.c --

#include <stdio.h>
#include <stdlib.h>

int main (void)
{
unsigned char buff[20];
unsigned int i;

i = 0xaabbccddUL;
*((int *)buff) = i; /* Is this UB ? */

Yes. There is no guarantee that buff is correctly aligned for an int.
You can either use memcpy(), or access the representation via (unsigned
char *) &i.

In theory, it's also possible that sizeof(int) is greater than 20,
which will cause obvious problems, but that's not likely to happen in
practice.
printf("buff: %x:%x:%x:%x\n", buff[0], buff[1], buff[2], buff[3]);

This assumes that an int is four bytes. This is not necessarily true,
and in this case the problem does occur in practice.
return EXIT_SUCCESS;
}

-- end listing --

Output:
buff: dd:cc:bb:aa

Output seems correct on my Little Endian PC.

In the statement: "*((int *)buff) = i; ", buff is casted to be treated
as an int *. Is this valid?

My compiler does not produce any diagnostics for the above
program but somebody else complained that their compiler
warns "casting of lvalue is deprecated".

That warning refers to an implementation-specific extension which
you're not using, so don't worry about that.
 
R

Richard Heathfield

(e-mail address removed) said:
Please see the code below

-- start listing is_it_ub.c --

#include <stdio.h>
#include <stdlib.h>

int main (void)
{
unsigned char buff[20];
unsigned int i;

i = 0xaabbccddUL;
*((int *)buff) = i; /* Is this UB ? */

Yes. You're evaluating buff (which is a pointer to char), and then
converting that value to a pointer to int, but there is no guarantee that
it will be properly aligned. You then dereference the possibly-invalid
pointer thus obtained. On systems where this works, it's harmless. On
systems where it doesn't, we're talking potential bus errors, which are
definite showstoppers.
 
W

Walter Roberson

unsigned char buff[20];
unsigned int i;
i = 0xaabbccddUL;
*((int *)buff) = i; /* Is this UB ? */

Yes, because you don't know that buff has been properly aligned
to write an int into the beginning of it.

Also, you are assuming that unsigned int is big enough to hold
something 32 bits wide; an unsigned int need only be 16 bits wide to
satisify the value constraints.

Your constant is written as an unsigned long and that is assigned
into the unsigned int. That's odd enough to stand out, but it should
not be a problem in practice: if unsigned long is wider than
unsigned int, the unsigned long will not have any sign extension,
and even if it did, that sign extension would effectively be removed
when assigned into the unsigned int.

Your assignment involves type punning on a plain int rather than
an unsigned int. There is the possibility that an arbitrary
unsigned int might become a trap representation when the same
bit pattern is used as an int; such problems become more -likely-
when the unsigned int has bits set that correspond to sign bits
in the int. The particular value you chose, 0xaabbccdd, is -likely-
to overlay the top bit of 0xaa or 0xcc onto the sign bit, so you -could-
be getting into trouble that way. Safer to convert through
unsigned int * .
 
G

Guest

Walter said:
unsigned char buff[20];
unsigned int i;
i = 0xaabbccddUL;
*((int *)buff) = i; /* Is this UB ? */
[...]
Your assignment involves type punning on a plain int rather than
an unsigned int. There is the possibility that an arbitrary
unsigned int might become a trap representation when the same
bit pattern is used as an int; such problems become more -likely-
when the unsigned int has bits set that correspond to sign bits
in the int. The particular value you chose, 0xaabbccdd, is -likely-
to overlay the top bit of 0xaa or 0xcc onto the sign bit, so you -could-
be getting into trouble that way. Safer to convert through
unsigned int * .

There is no possibility for a trap representation. The value is
converted to, not reinterpreted as, a signed int, which results in an
implementation-defined value (and a trap representation, by definition,
is not a value), or the raising of an implementation-defined signal. In
the former case, there's no UB. In the latter case, there is UB if the
signal is not handled, but even then, there is no trap representation.

That said, since padding bits aren't an issue (you know the
representation is a valid unsigned int), the only trap representations
a signed integer can have are all bits one, and all bits zero except
for the sign bit. Neither is possible here.
 
G

Guest

Harald said:
Walter said:
unsigned char buff[20];
unsigned int i;
i = 0xaabbccddUL;
*((int *)buff) = i; /* Is this UB ? */
[...]
Your assignment involves type punning on a plain int rather than
an unsigned int. There is the possibility that an arbitrary
unsigned int might become a trap representation when the same
bit pattern is used as an int; such problems become more -likely-
when the unsigned int has bits set that correspond to sign bits
in the int. The particular value you chose, 0xaabbccdd, is -likely-
to overlay the top bit of 0xaa or 0xcc onto the sign bit, so you -could-
be getting into trouble that way. Safer to convert through
unsigned int * .

There is no possibility for a trap representation. The value is
converted to, not reinterpreted as, a signed int, which results in an
implementation-defined value (and a trap representation, by definition,
is not a value), or the raising of an implementation-defined signal. In
the former case, there's no UB. In the latter case, there is UB if the
signal is not handled, but even then, there is no trap representation.

Correction: the wording actually only says the result is
implementation-defined, not that the result is an
implementation-defined value. However, this is no licence for
implementations to declare the result is undefined (as it would be in
the case of a trap representation).
 

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,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top