Thread: RE: [GD-General] The joy of type aliasing and C
Brought to you by:
vexxed72
|
From: Neil S. <ne...@r0...> - 2003-12-26 21:10:02
|
> $18 from ansi.org. The C++ standard is $183. I think that > sums up the differences between the two languages very succinctly =) I'm pretty sure the C++ standard is $18 as well. Although the language is at least 10 times more complicated than C. ;) - Neil. |
|
From: Stefan B. <ste...@di...> - 2003-12-29 10:57:41
|
I don't know about C99, but the C++ standard simply states that the
compiler is allowed to assume that there no aliasing between pointers of two
different types. This means that in the following snippet, the value of 'i'
may be different depending on how aggressive the compiler is when
optimising:
float f = 0.0f;
int* ip = (int*) f;
*ip = 2345;
f = 4532.3f;
int i = *ip;
This is a real issue that could cause very strange bugs on various
platforms (PS2 is the most common target where this can pop up).
However, as I said I am unsure about C. I don't think it has this rule as
it's been touted as one reason why C++ code can actually be quicker than C
code. C99 introduces restricted pointers which allow the same thing but I
believe you have to hint the compiler explicitly rather than relying on a
semi-obscure language rule.
/Stef!
--
Stefan Boberg
CTO @ Digital Illusions CE
-----Original Message-----
From: Brian Hook [mailto:ho...@py...]
Sent: den 26 december 2003 19:04
To: gam...@li...
Subject: [GD-General] The joy of type aliasing and C
About a year and a half ago there was a fairly major brouhaha on the
algorithms list about this line of code:
int x = * ( int * ) &somefloat;
Now, let's push aside endianess and size issues, the concern was that
since there was "type-aliasing" that Something Bad could happen.
Something Bad, of course, being a rather ambiguous statement.
I'm aware of all the bad things that can happen if you have
type-aliasing in conjunction with pointer aliasing, which is related,
but that one line above doesn't seem like it should be bad _with a
legal C compiler_.
The major concern are optimizations that the compiler may make that
affect order. For example:
somefloat = 1.0f;
x = * ( int * ) &somefloat;
In theory, a heavily optimizing C compiler would see that the
assignment to somefloat should not affect the assignment to x since
they are incompatible types, which may allow it to decide to assign to
somefloat _after_ the assignment to x.
But that would be illegal. The C specification states that the end of
every expression is a sequence point, and thus the assignment to
somefloat MUST be flushed before any subsequent statements are
executed.
So, I can buy that the aliasing thing is a serious concern if there is
concern about a C compiler aggressively optimizing and doing it
incorrectly, but that doesn't seem to be the argument.
Of course, granted, using a union makes more sense and is a bit
cleaner, I'm fine with that:
union
{
int i;
float somefloat;
} u;
u.somefloat = 1.0f;
x = u.i;
But according to the C standard, the above is undefined ("If the value
being stored in an object is accessed from another object that
overlaps in any way the storage of the first object, then the overlap
shall be exact and the two objects shall have qualified or unqualified
versions of a compatible type; otherwise, the behavior is
undefined.").
Anyone have something more authoritative on this issue?
Brian
-------------------------------------------------------
This SF.net email is sponsored by: IBM Linux Tutorials.
Become an expert in LINUX or just sharpen your skills. Sign up for IBM's
Free Linux Tutorials. Learn everything from the bash shell to sys admin.
Click now! http://ads.osdn.com/?ad_id78&alloc_id371&op=click
_______________________________________________
Gamedevlists-general mailing list
Gam...@li...
https://lists.sourceforge.net/lists/listinfo/gamedevlists-general
Archives:
http://sourceforge.net/mailarchive/forum.php?forum_idU7
|
|
From: Tom H. <to...@ve...> - 2003-12-29 19:21:25
|
At 02:57 AM 12/29/2003, you wrote: > I don't know about C99, but the C++ standard simply states that the >compiler is allowed to assume that there no aliasing between pointers of two >different types. Do you have a specific reference to this? Tom |
|
From: Brian H. <ho...@py...> - 2003-12-29 19:32:50
|
On Mon, 29 Dec 2003 11:20:31 -0800, Tom Hubina wrote: > At 02:57 AM 12/29/2003, you wrote: >> I don't know about C99, but the C++ standard simply states that >> the compiler is allowed to assume that there no aliasing between >> pointers of two different types. >> > > Do you have a specific reference to this? Both standards state this, kind of. In C99, it's section 6.5, paragraph 7, where it specifically says "An object shall have its stored value accessed only by an lvalue expression that has one of the following types:" At which point it lists all the types that are compatible with that retrieval. There's a footnote that specifically mentions "The intent of this is to specify those circumstances in which an object may or may not be aliased". Since integers and floats are not considered compatible types, that indicates that the compiler can assume that they cannot be aliased. In the C++ 98 standard, 3.10 paragraph 15, there's the statement "If a program attempts to access the stored value of an object through an lvalue of other than one of the following types the behaviour is undefined:" and it lists a very similar group to the C standard, and has a verbatim footnote of the intention footnote. The list a little bit longer simply because C++ has more type rules (e.g. base class accessed through child class pointer is legal), but is otherwise identical. Brian |
|
From: Alen L. <ale...@cr...> - 2003-12-30 07:04:49
|
>In C99, it's section 6.5, paragraph 7, where it specifically says "An >object shall have its stored value accessed only by an lvalue >expression that has one of the following types:" >In the C++ 98 standard, 3.10 paragraph 15, there's the statement "If a >program attempts to access the stored value of an object through an >lvalue of other than one of the following types the behaviour is >undefined:" Didn't see the whole thing, so perhaps this is a stupid question, but... is there any particular reason that they mention an lvalue? Because, as I understand, we are accessing the stored value of a float through an rvalue of type int, not lvalue of type int. (I mean, it could have just as well been (const int*), wouldn't make a difference.) Alen |
|
From: Crosbie F. <cr...@cy...> - 2003-12-30 09:49:40
|
> From: Alen Ladavac > > Didn't see the whole thing, so perhaps this is a stupid > question, but... is > there any particular reason that they mention an lvalue? Because, as I > understand, we are accessing the stored value of a float > through an rvalue of > type int, not lvalue of type int. (I mean, it could have just > as well been > (const int*), wouldn't make a difference.) int x = ( int & ) f; int x = * ( int * ) &f; In both cases 'f' is an lvalue (in this example). Perhaps one might avoid aliasing if one declared f as register or as volatile, but it's pretty tricky to get the binary representation of a float without going via an lvalue of some sort. Which then prompts the questions: 1) Is it possible to convert a float to its native binary representation without going via an lvalue (not even indirectly)? 2) What is the most portable way (that avoids risk of alias optimisation) of getting a float into binary representation, in native form and/or a particular standard form (IEEE, etc.)? |
|
From: Alen L. <ale...@cr...> - 2003-12-30 10:53:58
|
> int x = ( int & ) f; > int x = * ( int * ) &f; > > In both cases 'f' is an lvalue (in this example). But it doesn't have to be. int x = ( const int & ) f; int x = * ( const int * ) &f; would work just as well. Now, this depends on definition of lvalue. Perhaps (const int &) might be considered an lvalue, but according to the origin of the name, it should be rvalue (lvalue is by definition of name "value that can be used on the left side of assignment expression"). I didn't check the standard, but I guess that they define lvaue somewhat different, marking those two examples as "constant lvalue", and making my note pointless. Anyway, it was quite a digression. What might be of more interest is: What does C++98 say on using reinterpret_cast? i = reinterpret_cast<int&>(f); The reinterpret_cast operator is _designed_ to be used for type punning, isn't it? -- Alen |
|
From: Brian H. <ho...@py...> - 2003-12-31 17:06:53
|
> would work just as well. Now, this depends on definition of lvalue. >From 6.3.2.1: "An lvalue is an expression with an object type or an incomplete type other than void; if an lvalue does not designate an object when it is evaluated, the behavior is undefined....a modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member with a const-qualified type." >From footnote 53: "The name 'lvalue' comes originally from the assignment expression E1 =3D E2, in which the left operand E1 is required be a (modifiable) lvalue." > Perhaps (const int &) might be considered an lvalue, but according int x =3D ( int & ) f; int x =3D * ( int * ) &f; In the above, both the 'f' are considered lvalues, since they are expressions with an object type. > What might be of more interest is: What does C++98 say on using > reinterpret_cast? > > i =3D reinterpret_cast<int&>(f); That is semantically identical to a direct cast as in C and is equally unsafe since there is no guarantee that the bit representation of 'f' will be meaningful as an integer. That said, it does work on the compilers I've tried =3D) > The reinterpret_cast operator is _designed_ to be used for type > punning, isn't it? Typically only for modifying pointer types, e.g. derived pointer to base class, etc. Brian |
|
From: Alen L. <ale...@cr...> - 2004-01-03 08:57:20
|
>int x = ( int & ) f; >int x = * ( int * ) &f; >In the above, both the 'f' are considered lvalues, since they are >expressions with an object type. I thought so. :/ >> The reinterpret_cast operator is _designed_ to be used for type >> punning, isn't it? >Typically only for modifying pointer types, e.g. derived pointer to >base class, etc. But it does compile without errors when used to do type punning (FWIK). Weirdo. Is there some statement in the standard, ignored by compilers, that says it shouldn't? I'm not reading the standard, but some Deep C++ articles, saying: "While the Standard gives static_cast a sweeping general property plus a complicated set of exceptions, it limits reinterpret_cast to two fundamental roles: - Conversions to and from pointers - Overlaying an lvalue with multiple types (a.k.a. type punning) " So maybe that creates some confusion. Is the statement above correct? Alen |
|
From: Brian H. <ho...@py...> - 2004-01-03 16:15:05
|
> - Overlaying an lvalue with multiple types (a.k.a. type punning) " This is what it does, and it DOES compile, because it's legal syntax, just like pointer casting between incompatible types in C. But just because it compiles doesn't mean that the standard guarantees it will run like you expect =3D) Brian |
|
From: Brian H. <ho...@py...> - 2003-12-30 17:54:00
|
> 2) What is the most portable way (that avoids risk of alias
> optimisation) of getting a float into binary representation, in
> native form and/or a particular standard form (IEEE, etc.)?
float f =3D 1.0f;
const unsigned char *c =3D ( const unsigned char * ) &f;
int i;
for ( i =3D 0; i < sizeof( f ); i++ )
{
store_some_byte( c[ i ] );
}
That is 100% legal, as long as it stays in char form. That is how
you're expected to transmit it over, say, a network connection if
necessary, assuming the machine on the other side understands the same
floating point format.
Floating point in the C standard is borderline disastrous.
Factoid #1: signed integer representation is not specified in the
standard, so you could conceivably run on a system with, say,
ones-complement arithmetic.
Factoid #2: C99 introduced support for IEEE-754 floating point
operations and formats officially, but it's an optional extension that
can be examined by checking __STDC_IEC_559__
Factoid #3: There's a standard #pragma to tell the compiler not to
dork with the floating point control and status words while you do
stuff that relies on that state remaining constant.
Brian
|
|
From: Brian H. <ho...@py...> - 2003-12-26 21:16:53
|
>> $18 from ansi.org. The C++ standard is $183. I think that sums >> up the differences between the two languages very succinctly =3D) >> > > I'm pretty sure the C++ standard is $18 as well. No, I misspoke, it's $273. That is not a typo. http://webstore.ansi.org/ansidocstore/product.asp?sku=3DISO%2FIEC+14882% 3A2003 The C99 spec is only $18: http://webstore.ansi.org/ansidocstore/product.asp?sku=3DINCITS%2FISO%2FI EC+9899%2D1999 Brian |
|
From: Colin F. <cp...@ea...> - 2003-12-27 00:22:57
|
The fact that the C99 spec is $18, and the C++03 spec is $273,
has "conspiracy" written all over it!
Nobody buys the *latest* (Mwoohahahaaaa!) C++ specifications,
and, WHAM!, [insert greatest horror here] falls upon the Earth!!
(Screaming in terror) "C++ is made of people!!!"
P.S.: My "Faster Than Light" technology relies on type aliasing,
so naturally I had an interest in seeing that the space
aliens didn't infiltrate ANSI and change the C++ specification.
But my robots tell me that if I would just up my meds
that things would have a way of working themselves out for
the best. I think they can't handle the truth, which is
"out there", maybe in the C++03 specification.
|