Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   C Programming (http://www.velocityreviews.com/forums/f42-c-programming.html)
-   -   Is the aliasing rule symmetric? (http://www.velocityreviews.com/forums/t742376-is-the-aliasing-rule-symmetric.html)

Johannes Schaub (litb) 01-21-2011 01:46 PM

Is the aliasing rule symmetric?
 
Posting my SO question to usenet:

Hello all. I had a discussion with someone on IRC and this question turned
up. We are allowed by the Standard to change an object of type `int` by a
`char` lvalue.

int a;
char *b = (char*) &a;
*b = 0;

Would we be allowed to do this in the opposite direction, if we know that
the alignment is fine?

The issue I'm seeing is that the aliasing rule does not cover the simple
case of the following, if one considers the aliasing rule as a non-symmetric
relation

int a;
a = 0;

The reason is, that each object contains a sequence of `sizeof(obj)`
`unsigned char` objects (called the "object representation"). If we change
the `int`, we will change some or all of those objects. However, the
aliasing rule only states we are allowed to change a `int` by an `char` or
`unsigned char`, but not the other way around. Another example

int a[1];
int *ra = a;
*ra = 0;

Only one direction is described by 3.10/15 ("An aggregate or union type that
includes..."), but this time we need the other way around ("A type that is
the element or non-static data member type of an aggregate...").

Is the other direction implied? The above spec quotes are from C++, but I
beleive same things apply to C.

SG 01-21-2011 03:18 PM

Re: Is the aliasing rule symmetric?
 
On 21 Jan., 14:46, Johannes Schaub (litb) wrote:
>
> Hello all. I had a discussion with someone on IRC and this question turned
> up. We are allowed by the Standard to change an object of type `int` by a
> `char` lvalue.
>
> * * int a;
> * * char *b = (char*) &a;
> * * *b = 0;
>
> Would we be allowed to do this in the opposite direction, if we know that
> the alignment is fine?


I think the question boils down to another question: When does a POD
object start to exist? One could argue that once you have properly
aligned memory to hold an int object and the byte sequence represents
a valid state of an int object, there *is* an int object. Consider

int* p = (int*)malloc(sizeof int);
*p = 1729;

malloc returns properly aligned uninitialized memory. When does the
int object start to exist?

Under the assumption that an int objects exists at some address as
soon as the byte sequence starting at that address represents a valid
int object's state and the memory is properly aligned, the aliasing
rule is practically symmetric.

Cheers!
SG

Ben Bacarisse 01-21-2011 04:15 PM

Re: Is the aliasing rule symmetric?
 
"Johannes Schaub (litb)" <schaub-johannes@web.de> writes:

> Posting my SO question to usenet:


I don't find this question all that clear, but perhaps my confusion will
help make it clearer!

> Hello all. I had a discussion with someone on IRC and this question turned
> up. We are allowed by the Standard to change an object of type `int` by a
> `char` lvalue.
>
> int a;
> char *b = (char*) &a;
> *b = 0;


OK so far.

> Would we be allowed to do this in the opposite direction, if we know that
> the alignment is fine?


What's the opposite direction? Are you asking if changing the int will
change the value of *b? If so, yes (provided the new int value's bits
do indeed affect the byte in question).

If you mean can we change an array of char (suitable aligned) by writing
though and int *, then the answer is no.

> The issue I'm seeing is that the aliasing rule does not cover the simple
> case of the following, if one considers the aliasing rule as a non-symmetric
> relation
>
> int a;
> a = 0;


The wording in the C standard covers that case explicitly:

An object shall have its stored value accessed only by an lvalue
expression that has one of the following types:

-- a type compatible with the effective type of the object,
-- (etc...)

'a' is an lvalue expression compatible with the effective type of the
object denoted by 'a'.

C++ does not use the concept of effective type and it does not used C's
notion of compatible types, so there are four clauses in 3.10 p15 that
capture the same semantics as this one clause in the C standard.

> The reason is, that each object contains a sequence of `sizeof(obj)`
> `unsigned char` objects (called the "object representation"). If we change
> the `int`, we will change some or all of those objects. However, the
> aliasing rule only states we are allowed to change a `int` by an `char` or
> `unsigned char`, but not the other way around. Another example
>
> int a[1];
> int *ra = a;
> *ra = 0;
>
> Only one direction is described by 3.10/15 ("An aggregate or union type that
> includes..."), but this time we need the other way around ("A type that is
> the element or non-static data member type of an aggregate...").


I may be missing your point, but this case seems to me perfectly well
covered by the 6.5 p7 in C and 3.10 p15 in C++. The effective type of
*ra is int and that is compatible (actually the same as) the effective
type of the object being accessed. In C++ the access is through an
lvalue expression with the dynamic type of the object (both are int).

> Is the other direction implied? The above spec quotes are from C++, but I
> beleive same things apply to C.


--
Ben.

Johannes Schaub (litb) 01-21-2011 05:20 PM

Re: Is the aliasing rule symmetric?
 
Ben Bacarisse wrote:

> "Johannes Schaub (litb)" <schaub-johannes@web.de> writes:
>
>> Would we be allowed to do this in the opposite direction, if we know that
>> the alignment is fine?

>
> What's the opposite direction? Are you asking if changing the int will
> change the value of *b? If so, yes (provided the new int value's bits
> do indeed affect the byte in question).
>


I mean to ask: If aliasing of an A object by an lvalue of type B is OK, is
aliasing of a B object by an lvalue of type A OK?

> If you mean can we change an array of char (suitable aligned) by writing
> though and int *, then the answer is no.
>
>> The issue I'm seeing is that the aliasing rule does not cover the simple
>> case of the following, if one considers the aliasing rule as a
>> non-symmetric relation
>>
>> int a;
>> a = 0;

>
> The wording in the C standard covers that case explicitly:
>
> An object shall have its stored value accessed only by an lvalue
> expression that has one of the following types:
>
> -- a type compatible with the effective type of the object,
> -- (etc...)
>
> 'a' is an lvalue expression compatible with the effective type of the
> object denoted by 'a'.
>


C doesn't seem to have this wording. But C++ says:

The object representation of an object of type T is the sequence of N
unsigned char objects taken up by the object of type T, where N equals
sizeof(T).

However I may well be wrong here, and this is only a conceptual description,
and not really a description of actual unsigned char objects covering the
same storage. In that case, the below array case and the struct case show
other situations making my point.

>> The reason is, that each object contains a sequence of `sizeof(obj)`
>> `unsigned char` objects (called the "object representation"). If we
>> change the `int`, we will change some or all of those objects. However,
>> the aliasing rule only states we are allowed to change a `int` by an
>> `char` or `unsigned char`, but not the other way around. Another example
>>
>> int a[1];
>> int *ra = a;
>> *ra = 0;
>>
>> Only one direction is described by 3.10/15 ("An aggregate or union type
>> that includes..."), but this time we need the other way around ("A type
>> that is the element or non-static data member type of an aggregate...").

>
> I may be missing your point, but this case seems to me perfectly well
> covered by the 6.5 p7 in C and 3.10 p15 in C++. The effective type of
> *ra is int and that is compatible (actually the same as) the effective
> type of the object being accessed. In C++ the access is through an
> lvalue expression with the dynamic type of the object (both are int).
>


The above changes two objects. The first has type int, and the second has
type int[1]. The change of a int[1] by an lvalue of type int is not covered
anywhere at 3.10 p15 in C++. It's not covered by the C rules either, it
seems.

Same situation with structs:

struct A { int a; };
A a;
int *p = &a.a;
*p = 0;

This changes the stored value of an A object by an lvalue of type int. Where
is this covered?

Is my understanding of this wrong?


Ben Bacarisse 01-21-2011 06:44 PM

Re: Is the aliasing rule symmetric?
 
"Johannes Schaub (litb)" <schaub-johannes@web.de> writes:

> Ben Bacarisse wrote:
>
>> "Johannes Schaub (litb)" <schaub-johannes@web.de> writes:
>>
>>> Would we be allowed to do this in the opposite direction, if we know that
>>> the alignment is fine?

>>
>> What's the opposite direction? Are you asking if changing the int will
>> change the value of *b? If so, yes (provided the new int value's bits
>> do indeed affect the byte in question).
>>

>
> I mean to ask: If aliasing of an A object by an lvalue of type B is OK, is
> aliasing of a B object by an lvalue of type A OK?


No, not as far as I can see. Let's assume a system with sizeof(int) ==
1 and where int requires no more alignment than char. On such a system
this

int a;
char *cp = (void *)&a;
*cp = 42;

is fine by C's rules (and the intent is probably that C++ be the same in
this regard) but this

char a;
int *ip = (void *)&a;
*ip = 42;

is undefined. It is explicitly undefined by 6.5 p7 whereas the first is
explicitly permitted by that text.

>> If you mean can we change an array of char (suitable aligned) by writing
>> though and int *, then the answer is no.
>>
>>> The issue I'm seeing is that the aliasing rule does not cover the simple
>>> case of the following, if one considers the aliasing rule as a
>>> non-symmetric relation
>>>
>>> int a;
>>> a = 0;

>>
>> The wording in the C standard covers that case explicitly:
>>
>> An object shall have its stored value accessed only by an lvalue
>> expression that has one of the following types:
>>
>> -- a type compatible with the effective type of the object,
>> -- (etc...)
>>
>> 'a' is an lvalue expression compatible with the effective type of the
>> object denoted by 'a'.
>>

>
> C doesn't seem to have this wording.


What wording? The indented text is from section 6.5 paragraph 7 in
n1256.pdf (I don't have the "real thing").

> But C++ says:
>
> The object representation of an object of type T is the sequence of N
> unsigned char objects taken up by the object of type T, where N equals
> sizeof(T).


There is similar wording for C in 6.2.6.1 p4. It is not exactly the
same but I think it has pretty much the same effect. However, I am not
sure how this wording relates to your question.

> However I may well be wrong here, and this is only a conceptual description,
> and not really a description of actual unsigned char objects covering the
> same storage. In that case, the below array case and the struct case show
> other situations making my point.
>
>>> The reason is, that each object contains a sequence of `sizeof(obj)`
>>> `unsigned char` objects (called the "object representation"). If we
>>> change the `int`, we will change some or all of those objects. However,
>>> the aliasing rule only states we are allowed to change a `int` by an
>>> `char` or `unsigned char`, but not the other way around. Another example
>>>
>>> int a[1];
>>> int *ra = a;
>>> *ra = 0;
>>>
>>> Only one direction is described by 3.10/15 ("An aggregate or union type
>>> that includes..."), but this time we need the other way around ("A type
>>> that is the element or non-static data member type of an aggregate...").

>>
>> I may be missing your point, but this case seems to me perfectly well
>> covered by the 6.5 p7 in C and 3.10 p15 in C++. The effective type of
>> *ra is int and that is compatible (actually the same as) the effective
>> type of the object being accessed. In C++ the access is through an
>> lvalue expression with the dynamic type of the object (both are int).
>>

>
> The above changes two objects. The first has type int, and the second has
> type int[1]. The change of a int[1] by an lvalue of type int is not covered
> anywhere at 3.10 p15 in C++. It's not covered by the C rules either, it
> seems.


I see your point but I don't think that's what is intended. The object
being accessed is an int, not the array that contains it. The fact that
the access changes a larger containing object does not seem to alter the
validity of the access in question. Note the wording (in C) is all
about the access, not about what that access changes.

> Same situation with structs:
>
> struct A { int a; };
> A a;
> int *p = &a.a;
> *p = 0;
>
> This changes the stored value of an A object by an lvalue of type int. Where
> is this covered?
>
> Is my understanding of this wrong?


I think it is covered in C and in C++ because what is being accessed is
'a.a' not 'a'. The fact that a changes as a result is not ruled out by
the wording that permits the access of the sub-object.

--
Ben.

Dag-Erling Smørgrav 01-21-2011 08:35 PM

Re: Is the aliasing rule symmetric?
 
Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
> "Johannes Schaub (litb)" <schaub-johannes@web.de> writes:
> > I mean to ask: If aliasing of an A object by an lvalue of type B is
> > OK, is aliasing of a B object by an lvalue of type A OK?

> No, not as far as I can see.


The correct answer is "it depends". The OP used an example where B is
char and A is something larger, which is a special case that works in
one direction but not the other, but if e.g. A and B are struct types
with a common prefix, the answer is yes. A good real-world example is
the various flavors of struct sockaddr in the BSD socket API.

DES
--
Dag-Erling Smørgrav - des@des.no

Marcin Grzegorczyk 01-21-2011 09:45 PM

Re: Is the aliasing rule symmetric?
 
Johannes Schaub (litb) wrote:
> Hello all. I had a discussion with someone on IRC and this question turned
> up. We are allowed by the Standard to change an object of type `int` by a
> `char` lvalue.
>
> int a;
> char *b = (char*)&a;
> *b = 0;
>
> Would we be allowed to do this in the opposite direction, if we know that
> the alignment is fine?


Apparently not, as far as C is concerned. By 6.5p6 [all chapter and
verse references to N1256 unless indicated otherwise], "The effective
type of an object for an access to its stored value is the declared type
of the object, if any".

One consequence of the effective type rules is that the common technique
of providing a custom memory allocation scheme for freestanding
implementations, using a static array of unsigned char as the memory
pool, leads to undefined behaviour when you try to use memory allocated
on that pool as, say an array of ints -- even if you have a way to
specify the alignment (which C1X will provide). Indeed, the only
standard C way to get storage without declared type is malloc() and
friends, which of course need not be available in a freestanding
implementation.

[...]
> Another example
>
> int a[1];
> int *ra = a;
> *ra = 0;
>
> Only one direction is described by 3.10/15 ("An aggregate or union type that
> includes..."), but this time we need the other way around ("A type that is
> the element or non-static data member type of an aggregate...").
>
> Is the other direction implied? The above spec quotes are from C++, but I
> beleive same things apply to C.


Yes, C99 has the same wording (6.5p7). Incidentally, the ISO C
Committee has already acknowledged that this exact clause is confusing,
and even tried - twice - to improve the effective type rules (see WG14
documents N1409 and N1520); in both cases, the decision was "more work
needed".
--
Marcin Grzegorczyk

lawrence.jones@siemens.com 01-21-2011 10:23 PM

Re: Is the aliasing rule symmetric?
 
In comp.std.c "Johannes Schaub (litb)" <schaub-johannes@web.de> wrote:
>
> The above changes two objects. The first has type int, and the second has
> type int[1]. The change of a int[1] by an lvalue of type int is not covered
> anywhere at 3.10 p15 in C++. It's not covered by the C rules either, it
> seems.
>
> Same situation with structs:
>
> struct A { int a; };
> A a;
> int *p = &a.a;
> *p = 0;
>
> This changes the stored value of an A object by an lvalue of type int. Where
> is this covered?


I can't speak for C++, but those cases are both covered in C by the next
to last rule in 6.5p7 ("an aggregate or union type that includes one of
the aforementioned types...").
--
Larry Jones

Everything's gotta have rules, rules, rules! -- Calvin

Ben Bacarisse 01-21-2011 10:30 PM

Re: Is the aliasing rule symmetric?
 
Dag-Erling Smørgrav <des@des.no> writes:

> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>> "Johannes Schaub (litb)" <schaub-johannes@web.de> writes:
>> > I mean to ask: If aliasing of an A object by an lvalue of type B is
>> > OK, is aliasing of a B object by an lvalue of type A OK?

>> No, not as far as I can see.

>
> The correct answer is "it depends". The OP used an example where B is
> char and A is something larger, which is a special case that works in
> one direction but not the other, but if e.g. A and B are struct types
> with a common prefix, the answer is yes. A good real-world example is
> the various flavors of struct sockaddr in the BSD socket API.


Maybe there is C/C++ difference here but in C accessing a struct A as
if it were a struct B is undefined[1]. The old BSD socket API is going
to work because the implementation can't use the permission that the
aliasing rule give it in any way that would defeat the code. However,
in code such as this:

struct s1 { int i; double d; };
struct s2 { int i; float f; };

int f(struct s1 *s1p, struct s2 *s2p)
{
int x = s1p->i;
s2p->i = 42;
return x * s1p->i;
}

an implementation is permitted to assume that s1p->i has not changed and
to re-use the value of x in the return.

In some sense you are correct to say that it depends because there are
examples where Johannes Schaub's rule is OK (for example when A is char
and B is unsigned char) but I took the question to be a general one: is
it always OK to reverse the types? That's why I said "no" and gave a
single counter-example.

[1] There is a special clause in C about structs that share an initial
segment when both are members of the same union, but that is not what
you are talking about.

--
Ben.

Joshua Maurice 01-21-2011 11:36 PM

Re: Is the aliasing rule symmetric?
 
On Jan 21, 9:20*am, "Johannes Schaub (litb)" <schaub-johan...@web.de>
wrote:
> Ben Bacarisse wrote:
> > "Johannes Schaub (litb)" <schaub-johan...@web.de> writes:

>
> >> Would we be allowed to do this in the opposite direction, if we know that
> >> the alignment is fine?

>
> > What's the opposite direction? *Are you asking if changing the int will
> > change the value of *b? *If so, yes (provided the new int value's bits
> > do indeed affect the byte in question).

>
> I mean to ask: If aliasing of an A object by an lvalue of type B is OK, is
> aliasing of a B object by an lvalue of type A OK?


No. Don't think about it as an aliasing rule. Think about it as a rule
which restricts the types of lvalues with which you can legally access
objects.

You can always access an object through a char or unsigned char
lvalue. (Or maybe it's only for POD types - there's no consensus. I
would only use char and unsigned char to access POD objects.)

You can always access an object through a base class lvalue, but you
can never do the reverse: you can never take a complete object of type
T and access it through a derived type of type T.


All times are GMT. The time now is 04:34 PM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.