473,791 Members | 2,881 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

gcc, aliasing rules and unions

I'm getting horribly lost in the strict aliasing rules.
Is this code correct?

struct A { int x; };
struct B { int x, y; };

int foo( struct A *a ) {
struct B *b = (struct B *) a;
return b->x - b->y;
}

int bar(int x, int y)
{
struct B b1;
b1.x = x;
b1.y = y;
return foo( (struct A *) &b1 );
}

int baz(int x, int y)
{
union {
struct A a2;
struct B b2;
} u;
u.b2.x = x;
u.b2.y = y;
return foo( &u.a2 );
}

bar() seems correct. A struct pointer can be converted to another
struct pointer type and back again (C99 6.2.5p26 and 6.3.2.3p7), and
foo() accesses the b1 object through its effective type (the type it was
created/stored with, 6.5p6-p7).

But is baz() correct? gcc -O2 -Wall warns about bar(): "dereferenc ing
type-punned pointer will break strict-aliasing rules". The gcc manual
seems to suggest it should be converted to baz(), though its example is
about code which is non-standard either way. But I can't quite find
anything in the standard which says baz() is OK. It was all so simple
before the strict aliasing rules...
Maybe &u.a2 works like (struct A *)&u.b2 so that it can be cast "back"
to struct B*? It has the same address, but I can't find where that
helps for the aliasing rules. But if so, baz() is fine.

It would be OK for baz() but not for foo() to access u.a2.x, according
to 6.5.2.3p5 and 6.5.2.3p8. But that's not what this code does. Baz()
uses a2 itself, not a2.x - and it doesn't access it, it takes it
address.

Also I don't know what the "effective type" of a union member is. I
_thought_ it was the type of the last stored value, but I can't find
that in the Standard. 6.5p6 applies to objects with "no declared type",
which does not seem to fit union members.
Here are the texts I used to confuse myself with:

Gcc manual, in Info node Optimize Options or
<http://gcc.gnu.org/onlinedocs/gcc-4.0.2/gcc/Optimize-Options.html>:

-fstrict-aliasing

Allows the compiler to assume the strictest aliasing rules
applicable to the language being compiled. For C (and C++), this
activates optimizations based on the type of expressions. In
particular, an object of one type is assumed never to reside at the same
address as an object of a different type, unless the types are almost
the same. For example, an unsigned int can alias an int, but not a
void* or a double. A character type may alias any other type.

Pay special attention to code like this:

union a_union {
int i;
double d;
};
int f() {
a_union t;
t.d = 3.0;
return t.i;
}

The practice of reading from a different union member than the one
most recently written to (called "type-punning") is common. Even with
-fstrict-aliasing, type-punning is allowed, provided the memory is
accessed through the union type. So, the code above will work as
expected. However, this code might not:

int f() {
a_union t;
int* ip;
t.d = 3.0;
ip = &t.i;
return *ip;
}
C99 Standard:

6.2.5p26: All pointers to structure types shall have the same
representation and alignment requirements as each other.

6.3.2.3p7: A pointer to an object (...) may be converted to a pointer to
a different object (...). If the resulting pointer is not correctly
aligned[57] for the pointed-to type, the behavior is undefined.
Otherwise, when converted back again, the result shall compare equal
to the original pointer.

[39] The same representation and alignment requirements are meant to
imply interchangeabil ity as arguments to functions, return values
from functions, and members of unions.

[57] In general, the concept "correctly aligned" is transitive: if a
pointer to type A is correctly aligned for a pointer to type B,
which in turn is correctly aligned for a pointer to type C, then a
pointer to type A is correctly aligned for a pointer to type C.

6.5p6: The effective type of an object for an access to its stored value
is the declared type of the object, if any.[72] If a value is
stored into an object having no declared type through an lvalue
having a type that is not a character type, then the type of the
lvalue becomes the effective type of the object for that access and
for subsequent accesses that do not modify the stored value. (...)
For all other accesses to an object having no declared type, the
effective type of the object is simply the type of the lvalue used
for the access.

[72] Allocated objects have no declared type.

6.5p7: An object shall have its stored value accessed only by an lvalue
expression that has one of the following types:[73]
- a type compatible with the effective type of the object,
(...)
- an aggregate or union type that includes one of the aforementioned
types among its members (including, recursively, a member of a
subaggregate or contained union),
(...)

[73] The intent of this list is to specify those circumstances in which
an object may or may not be aliased.

6.5.2.3p5: One special guarantee is made in order to simplify the use of
unions: if a union contains several structures that share a common
initial sequence (see below), and if the union object currently
contains one of these structures, it is permitted to inspect the
common initial part of any of them anywhere that a declaration of
the complete type of the union is visible.

6.5.2.3p8 EXAMPLE 3:
The following is not a valid fragment (because the union type is
not visible within function f):
struct t1 { int m; };
struct t2 { int m; };
int f(struct t1 * p1, struct t2 * p2)
{
if (p1->m < 0)
p2->m = -p2->m;
return p1->m;
}
int g()
{
union { struct t1 s1;
struct t2 s2;
} u;
/* ... */
return f(&u.s1, &u.s2);
}

--
Hallvard
Apr 18 '06 #1
3 4781
On 2006-04-18, Hallvard B Furuseth <h.**********@u sit.uio.no> wrote:
I'm getting horribly lost in the strict aliasing rules.
Is this code correct?

struct A { int x; };
struct B { int x, y; };

int foo( struct A *a ) {
struct B *b = (struct B *) a;
return b->x - b->y;
}

int bar(int x, int y)
{
struct B b1;
b1.x = x;
b1.y = y;
return foo( (struct A *) &b1 );
}

int baz(int x, int y)
{
union {
struct A a2;
struct B b2;
} u;
u.b2.x = x;
u.b2.y = y;
return foo( &u.a2 );
}

bar() seems correct. A struct pointer can be converted to another
struct pointer type and back again (C99 6.2.5p26 and 6.3.2.3p7), and
foo() accesses the b1 object through its effective type (the type it was
created/stored with, 6.5p6-p7).

But is baz() correct? gcc -O2 -Wall warns about bar(): "dereferenc ing
type-punned pointer will break strict-aliasing rules". The gcc manual
seems to suggest it should be converted to baz(), though its example is
about code which is non-standard either way. But I can't quite find
anything in the standard which says baz() is OK. It was all so simple
before the strict aliasing rules...


As far as I know both are equally correct from the point of the view of
the standard. The idea of "assume no aliasing between pointers to
objects where the types of the objects are reasonably different" is a
gcc optimization. I don't know if the standard says anything about this
(e.g. whether it recommends this particular optimization).

Technically I think the compiler always has to expect aliasing between
pointers (unless you use restrict, I don't know if restrict is in any of
the standards), and gcc on high optimization levels may produce
incorrect code if you do pointer casts to access one object as an object
of an unrelated type. You might get the problem in code like this:

int f(struct B* b)
{
...
struct A* a = (struct A*) b;
b->x++;
return a->x;
}

gcc may have assumed that *a is not the same object as *b (because why
would anyone store a struct A in a location pointed to by a struct B
*?), and therefore have failed to recognize the data dependence between
the second and third lines. It may then have incorrectly reordered the
operations so that b->x++ happens after a->x is returned instead of
before.

People write code like this sometimes when they want to simulate something
like C++ inheritance in C. I think using unions for this kind of thing is
nicer style anyway, and works better with gcc.

It's better to fix the code so the warnings go away and leave the
optimizations turned on if you can.
Apr 18 '06 #2
Sorry to disappear like that...

Ben, Gcc makes the "assume no aliasing between pointers to objects where
the types of the objects are reasonably different" optimization because
the standard allows it. That's what paragraphs 6.5p7 and so on which I
quoted are all about. If the standard dit not allow it, 'gcc -O2' would
be very broken in doing this. So my original question remains:

Ben C writes:
On 2006-04-18, Hallvard B Furuseth <h.**********@u sit.uio.no> wrote:
I'm getting horribly lost in the strict aliasing rules.
Is this code correct?

struct A { int x; };
struct B { int x, y; };

int foo( struct A *a ) {
struct B *b = (struct B *) a;
return b->x - b->y;
}

int bar(int x, int y)
{
struct B b1;
b1.x = x;
b1.y = y;
return foo( (struct A *) &b1 );
}

int baz(int x, int y)
{
union {
struct A a2;
struct B b2;
} u;
u.b2.x = x;
u.b2.y = y;
return foo( &u.a2 );
}

bar() seems correct. A struct pointer can be converted to another
struct pointer type and back again (C99 6.2.5p26 and 6.3.2.3p7), and
foo() accesses the b1 object through its effective type (the type it was
created/stored with, 6.5p6-p7).

But is baz() correct? gcc -O2 -Wall warns about bar(): "dereferenc ing
type-punned pointer will break strict-aliasing rules". The gcc manual
seems to suggest it should be converted to baz(), though its example is
about code which is non-standard either way. But I can't quite find
anything in the standard which says baz() is OK. It was all so simple
before the strict aliasing rules...
As far as I know both are equally correct from the point of the view of
the standard. The idea of "assume no aliasing between pointers to
objects where the types of the objects are reasonably different" is a
gcc optimization. I don't know if the standard says anything about this
(e.g. whether it recommends this particular optimization).


The standard allows it in some circumstances - and as I said, as far as
I can tell it allows in baz() and that makes baz() buggy, even though
gcc recommends baz() over bar().
Technically I think the compiler always has to expect aliasing between
pointers (unless you use restrict, I don't know if restrict is in any
of the standards), and gcc on high optimization levels may produce
incorrect code if you do pointer casts to access one object as an
object of an unrelated type.


No, as I said, the compiler may assume that objects of different types
are not aliased, with some exceptions.

--
Hallvard
May 2 '06 #3
On 2006-05-02, Hallvard B Furuseth <h.**********@u sit.uio.no> wrote:
Sorry to disappear like that...

Ben, Gcc makes the "assume no aliasing between pointers to objects where
the types of the objects are reasonably different" optimization because
the standard allows it. That's what paragraphs 6.5p7 and so on which I
quoted are all about. If the standard dit not allow it, 'gcc -O2' would
be very broken in doing this. So my original question remains:
Ah sorry, yes I didn't read your original post properly.
Ben C writes:
On 2006-04-18, Hallvard B Furuseth <h.**********@u sit.uio.no> wrote:
I'm getting horribly lost in the strict aliasing rules.
Is this code correct?

struct A { int x; };
struct B { int x, y; };

int foo( struct A *a ) {
struct B *b = (struct B *) a;
return b->x - b->y;
}

int bar(int x, int y)
{
struct B b1;
b1.x = x;
b1.y = y;
return foo( (struct A *) &b1 );
}

int baz(int x, int y)
{
union {
struct A a2;
struct B b2;
} u;
u.b2.x = x;
u.b2.y = y;
return foo( &u.a2 );
}


[snip]

As you said (I think) in your original post, baz() should be incorrect
inasmuch as it's similar to this:

6.5.2.3p8 EXAMPLE 3:
The following is not a valid fragment (because the union type is
not visible within function f):
struct t1 { int m; };
struct t2 { int m; };
int f(struct t1 * p1, struct t2 * p2)
{
if (p1->m < 0)
p2->m = -p2->m;
return p1->m;
}
int g()
{
union { struct t1 s1;
struct t2 s2;
} u;
/* ... */
return f(&u.s1, &u.s2);
}

I tried compiling the actual code in the example in 6.5.2.3p8, and gcc
didn't warn about that either.

It may be that gcc is not trying to do any aliasing optimization in this
case, and so that's why it doesn't warn.

After all, you don't get the warning about bar's call to foo either if
you compile on -O0. This was the example:

int foo( struct A *a ) {
struct B *b = (struct B *) a;
return b->x - b->y;
}

int bar(int x, int y)
{
struct B b1;
b1.x = x;
b1.y = y;
return foo( (struct A *) &b1 );
}

In other words, gcc isn't trying to be a sort of lint that tells you if
you're keeping to the standard or not, it just tries to tell when it
might be compiling something you didn't expect.

I think baz() is wrong and gcc doesn't care.
May 2 '06 #4

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

4
1983
by: Yuri Victorovich | last post by:
In short my question is: If I overload "operator new" for class A and return from it instance of struct B (unrelated with A) as allocated memory area for A should aliasing rules work and allow optimizer to "merge" assemblies together ? My opinion: NO, since aliasing rules talk about one lvalue for access to two unrelated objects and one of these objects is not constructed yet w/in operator new.
2
2466
by: Bryan Parkoff | last post by:
I create one 32 Bits variable and four pointer variables. Four pointer variables link or point to one 32 Bits variable. Each pointer variable is 8 Bits. Look at my example below. unsigned int AA = 0; unsigned char* const AA_Byte1 = (unsigned char*)&AA; unsigned char* const AA_Byte2 = (unsigned char*)&AA + 1; unsigned char* const AA_Byte3 = (unsigned char*)&AA + 2; unsigned char* const AA_Byte4 = (unsigned char*)&AA + 3;
9
1975
by: Adam Warner | last post by:
Hi all, Message ID <c1qo3f0tro@enews2.newsguy.com> is one of many informative articles by Chris Torek about C. The particular message discusses aliasing and concludes with this paragraph: Under these strict type-aliasing rules, casting from (e.g.) "int *" to "short *" is not only quite suspicious, it is also likely to cause puzzling behavior, at least if you expect your "short *" to access or modify your "int". Even the time-honored,...
20
3771
by: nicolas.riesch | last post by:
I try to understand strict aliasing rules that are in the C Standard. As gcc applies these rules by default, I just want to be sure to understand fully this issue. For questions (1), (2) and (3), I think that the answers are all "yes", but I would be glad to have strong confirmation. About questions (4), (5) and (6), I really don't know. Please help ! ! !
9
2080
by: liljencrantz | last post by:
Hi, I have a piece of code that uses hashtables to store pointers to various bits of data. The hashtable sees all pointers as const void *, while the application obviously uses various other pointer types for the data. I've run into a warning with the following code: void hash_remove( hash_table_t *h, const void *key, const void **old_key,
10
2002
by: Old Wolf | last post by:
Consider the following program: #include <stdio.h> int main(void) { /* using malloc to eliminate alignment worries */ unsigned long *p = malloc( sizeof *p ); if ( p && sizeof(long) == sizeof(int) )
3
29699
by: David Mathog | last post by:
I have a program for which this line: if(! lstrtol(&atoken,length-2,(long *) &(lclparams->pad)) || (lclparams->pad< 0)){ generates the warning below, but ONLY if the gcc compiler is at -O2 or -O3. I don't see any reason why optimization should change things much in this piece of code - there's no way to optimize it out and I have verified that this particular line does what it should no matter how the program is compiled. Anyway,...
3
2922
by: Squat'n Dive | last post by:
Does anyone have an idea why -fno-strict-aliasing is turned off when cross compiling? in configure generated for 2.4.4: case $GCC in yes) # Python violates C99 rules, by casting between incompatible # pointer types. GCC may generate bad code as a result of that, # so use -fno-strict-aliasing if supported.
4
3226
by: Paul Brettschneider | last post by:
Hello all, consider the following code: typedef char T; class test { T *data; public: void f(T, T, T); void f2(T, T, T);
0
9669
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
9515
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
1
10154
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
9993
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
9029
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
0
6776
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
1
4109
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
3713
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
2913
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.