Is the aliasing rule symmetric?

Discussion in 'C Programming' started by Johannes Schaub (litb), Jan 21, 2011.

  1. 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.
     
    Johannes Schaub (litb), Jan 21, 2011
    #1
    1. Advertising

  2. Johannes Schaub (litb)

    SG Guest

    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
     
    SG, Jan 21, 2011
    #2
    1. Advertising

  3. "Johannes Schaub (litb)" <> 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.
     
    Ben Bacarisse, Jan 21, 2011
    #3
  4. Ben Bacarisse wrote:

    > "Johannes Schaub (litb)" <> 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?
     
    Johannes Schaub (litb), Jan 21, 2011
    #4
  5. "Johannes Schaub (litb)" <> writes:

    > Ben Bacarisse wrote:
    >
    >> "Johannes Schaub (litb)" <> 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.
     
    Ben Bacarisse, Jan 21, 2011
    #5
  6. Ben Bacarisse <> writes:
    > "Johannes Schaub (litb)" <> 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 -
     
    Dag-Erling Smørgrav, Jan 21, 2011
    #6
  7. 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
     
    Marcin Grzegorczyk, Jan 21, 2011
    #7
  8. Johannes Schaub (litb)

    Guest

    In comp.std.c "Johannes Schaub (litb)" <> 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
     
    , Jan 21, 2011
    #8
  9. Dag-Erling Smørgrav <> writes:

    > Ben Bacarisse <> writes:
    >> "Johannes Schaub (litb)" <> 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.
     
    Ben Bacarisse, Jan 21, 2011
    #9
  10. On Jan 21, 9:20 am, "Johannes Schaub (litb)" <>
    wrote:
    > Ben Bacarisse wrote:
    > > "Johannes Schaub (litb)" <> 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.
     
    Joshua Maurice, Jan 21, 2011
    #10
  11. On Jan 21, 2:23 pm, wrote:
    > In comp.std.c "Johannes Schaub (litb)" <> 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...").


    I can't speak to the exact standardeze offhand, but accessing an
    object through an appropriately created lvalue referring to a sub-
    object is perfectly sensible behavior. "*p" is an appropriately
    obtained lvalue which refers to a sub-object of the complete object,
    and it's perfectly fine to access the complete object through that
    lvalue to sub-object.

    By appropriately obtained, I mean that it actually "points to" the sub-
    object. Ex:

    struct Foo { int a; int b; };
    int main()
    {
    Foo f;
    int* x = reinterpret_cast<int*>(&f) + 1;
    *x = 1;
    return f.b;
    }

    The above is definitely not portable, and has undefined behavior on at
    least some platforms. There's no guarantee that "*x" actually refers
    to the sub-object "f.b". You need to obtain it through "appropriate"
    means, such as:

    struct Foo { int a; int b; };
    int main()
    {
    Foo f;
    int* x = & f.b;
    *x = 1;
    return f.b;
    }
     
    Joshua Maurice, Jan 21, 2011
    #11
  12. wrote:

    > In comp.std.c "Johannes Schaub (litb)" <> 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...").


    Well, that's the exact other way around. I'm using an int lvalue to access
    the value of an object whose effective type is A. But that rule only grants
    me to use an lvalue of type A to change the stored value of the member
    object whose effective type is int.

    Am I missing something?
     
    Johannes Schaub (litb), Jan 21, 2011
    #12
  13. Johannes Schaub (litb)

    James Kuyper Guest

    On 01/21/2011 05:30 PM, Ben Bacarisse wrote:
    > Dag-Erling Smørgrav<> writes:
    >
    >> Ben Bacarisse<> writes:
    >>> "Johannes Schaub (litb)"<> 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].

    ....
    > [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.


    I think that this is precisely what he was was referring to as a "common
    prefix".

    --
    James Kuyper
     
    James Kuyper, Jan 22, 2011
    #13
  14. Johannes Schaub (litb)

    James Kuyper Guest

    On 01/21/2011 06:48 PM, Johannes Schaub (litb) wrote:
    > wrote:
    >
    >> In comp.std.c "Johannes Schaub (litb)"<> 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...").

    >
    > Well, that's the exact other way around. I'm using an int lvalue to access
    > the value of an object whose effective type is A. But that rule only grants
    > me to use an lvalue of type A to change the stored value of the member
    > object whose effective type is int.
    >
    > Am I missing something?


    No - the reverse is not allowed: trying to access an object of type int
    using an lvalue of type struct A has undefined behavior. Consider that
    struct A might be padded to an alignment larger than sizeof(int), and
    consider that many of the operations permitted on an lvalue of struct
    type can access bytes that are part of that struct, but not part of the
    'a' member.

    It's unlikely to be true in this case, but it would be more likely if
    'a' were a smaller non-character type, such as a 16-bit short, on a
    machine with a large word size, say 64-bits. A compiler for such a
    machine might pad a struct 'A' to 64 bits. Then, when writing the value
    of the a member of a struct 'A', it might write the entire 64 bits,
    confident that the extra 48 bits would end up in memory reserved for the
    struct. That will cause problems if your lvalue of type struct A
    actually refers to memory containing a short that was an element in an
    array of shorts. In that case, those other 48 bits would contain other
    elements of that array.

    You can prove the existence of a similar aliasing problem involving
    arrays and their elements, by simply exchanging the roles of the array
    type and the struct type in the above argument.

    --
    James Kuyper
     
    James Kuyper, Jan 22, 2011
    #14
  15. Johannes Schaub (litb)

    James Kuyper Guest

    On 01/21/2011 06:36 PM, Joshua Maurice wrote:
    ....
    > 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.


    What distinction is there between an aliasing rule and that description?
    That description seems, to me, to be a fairly good definition of what
    "aliasing rule" means, at least in the context of C or C++.

    > 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.)


    I don't have a copy of the current C++ standard, nor of the latest draft
    of the next standard - the closest that I have is n3035.pdf. In 3.10p15,
    it makes the same exception for access through an lvalue of char and
    unsigned char type that C does, and that exception is not tied to the
    POD-ness of the dynamic type.

    --
    James Kuyper
     
    James Kuyper, Jan 22, 2011
    #15
  16. James Kuyper wrote:

    > On 01/21/2011 06:48 PM, Johannes Schaub (litb) wrote:
    >> wrote:
    >>
    >>> In comp.std.c "Johannes Schaub (litb)"<> 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...").

    >>
    >> Well, that's the exact other way around. I'm using an int lvalue to
    >> access the value of an object whose effective type is A. But that rule
    >> only grants me to use an lvalue of type A to change the stored value of
    >> the member object whose effective type is int.
    >>
    >> Am I missing something?

    >
    > No - the reverse is not allowed: trying to access an object of type int
    > using an lvalue of type struct A has undefined behavior.


    That's not true from an aliasing point of view. 3.10/15 explicitly allows
    this:

    an aggregate or union type that includes one of the aforementioned
    types among its elements or non-static data members

    > Consider that
    > struct A might be padded to an alignment larger than sizeof(int), and
    > consider that many of the operations permitted on an lvalue of struct
    > type can access bytes that are part of that struct, but not part of the
    > 'a' member.
    >


    We would then violate alignment requirements. This is a different thing from
    aliasing requirement though.
     
    Johannes Schaub (litb), Jan 22, 2011
    #16
  17. Johannes Schaub (litb)

    Seebs Guest

    On 2011-01-22, James Kuyper <> wrote:
    > On 01/21/2011 06:36 PM, Joshua Maurice wrote:
    > ...
    >> 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.


    > What distinction is there between an aliasing rule and that description?
    > That description seems, to me, to be a fairly good definition of what
    > "aliasing rule" means, at least in the context of C or C++.


    return foo(int *i1, int *i2) {
    *i1 = 1;
    *i2 = 2;
    return *i1;
    }

    The reason this might return either 1 or 2 is aliasing, but has nothing
    to do with the types with which you can legally access objects.

    -s
    --
    Copyright 2010, all wrongs reversed. Peter Seebach /
    http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
    http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
    I am not speaking for my employer, although they do rent some of my opinions.
     
    Seebs, Jan 22, 2011
    #17
  18. On Jan 22, 11:36 am, Seebs <> wrote:
    > On 2011-01-22, James Kuyper <> wrote:
    >
    > > On 01/21/2011 06:36 PM, Joshua Maurice wrote:
    > > ...
    > >> 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.

    > > What distinction is there between an aliasing rule and that description?
    > > That description seems, to me, to be a fairly good definition of what
    > > "aliasing rule" means, at least in the context of C or C++.

    >
    >         return foo(int *i1, int *i2) {
    >                 *i1 = 1;
    >                 *i2 = 2;
    >                 return *i1;
    >         }
    >
    > The reason this might return either 1 or 2 is aliasing, but has nothing
    > to do with the types with which you can legally access objects.
    >
    > -s
    > --
    > Copyright 2010, all wrongs reversed.  Peter Seebach / ://www.seebs.net/log/<-- lawsuits, religion, and funny pictureshttp://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
    > I am not speaking for my employer, although they do rent some of my opinions.


    Apparently it was cross-posted, and I didn't notice, and someone
    replied without the cross posting, and I definitely didn't notice.

    Seebs is right that
    int foo(int *i1, int *i2) {
    *i1 = 1;
    *i2 = 2;
    return *i1;
    }
    If i1 and i2 alias, then it returns 2. If they don't alias, then it
    returns 1. This function doesn't have a violation of the "strict
    aliasing rules", which would be better called "effective type access
    rules".

    Let's consider this function though:
    int foo(int* x, short* y)
    {
    *x = 1;
    *y = 2;
    return 1;
    }
    int bar(int* x, short* y)
    {
    *x = 1;
    *y = 2;
    return *x;
    }
    Let's consider functions foo and bar. Let's suppose that x and y alias
    in both. For function foo, there is no undefined behavior even though
    both alias (at least according to what appears to be the prominent
    interpretation of these rules). For function bar, if they alias, then
    we have undefined behavior. Both have aliasing of short* and int*, but
    only one has undefined behavior. It has undefined behavior because
    there is a read of a short object through an int lvalue in function
    bar.

    Obviously aliasing is a required component of this analysis, but the
    undefined behavior results from using an lvlaue to access an object.
    You can use a char lvalue to access an int object, but you cannot use
    an int lvalue to access a char object (or char array).
     
    Joshua Maurice, Jan 22, 2011
    #18
  19. James Kuyper <> writes:

    > On 01/21/2011 05:30 PM, Ben Bacarisse wrote:
    >> Dag-Erling Smørgrav<> writes:
    >>
    >>> Ben Bacarisse<> writes:
    >>>> "Johannes Schaub (litb)"<> 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].

    > ...
    >> [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.

    >
    > I think that this is precisely what he was was referring to as a
    > "common prefix".


    I don't see how it can be. For one thing, the various flavors of
    struct sockaddr are not in a union object (at least they were not used
    in that way the last time I used the BSD API).

    But more importantly, the special exception is not an exception to the
    effective type access rules. It is always possible (from the point of
    view of these rules) to access a member of a union that is not the one
    last stored -- the bits are simply reinterpreted (possibly as a value of
    a different type. That is as true for structures that don't share a
    common prefix as it is for ones that do (and it's true for non-structure
    types as well). What 6.5.2.3 p5 does is ensure that there won't be any
    surprises about what data you actually get when there is a common prefix.

    If the exception given in 6.5.2.3 p5 were not there, the access itself
    to these common prefix elements would not suddenly become undefined --
    the only difference would be that you would not be able to rely on
    getting the data you expect.

    --
    Ben.
     
    Ben Bacarisse, Jan 23, 2011
    #19
  20. Johannes Schaub (litb)

    James Kanze Guest

    On Jan 22, 12:08 pm, James Kuyper <> wrote:
    > On 01/21/2011 06:36 PM, Joshua Maurice wrote:
    > ...


    > > 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.


    > What distinction is there between an aliasing rule and that description?
    > That description seems, to me, to be a fairly good definition of what
    > "aliasing rule" means, at least in the context of C or C++.


    > > 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.)


    > I don't have a copy of the current C++ standard, nor of the latest draft
    > of the next standard - the closest that I have is n3035.pdf. In 3.10p15,
    > it makes the same exception for access through an lvalue of char and
    > unsigned char type that C does, and that exception is not tied to the
    > POD-ness of the dynamic type.


    The C++ standard makes the exception allowing access through
    a char or unsigned char type in the case where "a program
    attempts access the stored value of an object". I'm not sure of
    the exact intent, but accessing the stored value means a read
    access. The standard doesn't seem to say anything about
    modifying, except for the case where the access is to
    a nonmodifiable lvalue. I would hope that the intent would be
    more or less:

    float f = 3.14159;
    unsigned char* pc = reinterpret_cast<unsigned char*>(&f);
    printf("%u\n", *pc); // Legal.
    *pc = 0xFF; // Legal? Or UB?
    std::cout << f << std::endl; // UB (maybe a trapping NaN)

    There is (or should be) a clear exception, however, for memcpy
    like operations: the following must be legal:

    float f = 3.14159;
    unsigned char buff[sizeof(float)];
    memcpy(buff, &f, sizeof(float));
    float other;
    memcpy(&other, buff, sizeof(float));
    printf("%.5f\n", other); // Must output 3.14159

    This is in §3.9/3 in the C++ standard, and only for PODs in
    C++. In this case, I'm fairly certain that the intent is for
    C and C++ to be compatible in this regard, at least for PODs.

    At any rate, the above more or less corresponds to what actually
    happens on existing hardware. (To have similar problems with
    int, you need some fairly exotic hardware; I think a Univac MPS
    might fill the bill.)

    --
    James Kanze
     
    James Kanze, Jan 24, 2011
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Gazelle
    Replies:
    0
    Views:
    2,847
    Gazelle
    Nov 12, 2003
  2. Replies:
    0
    Views:
    1,391
  3. Johannes Schaub (litb)

    Is the aliasing rule symmetric?

    Johannes Schaub (litb), Jan 21, 2011, in forum: C++
    Replies:
    2
    Views:
    296
    Joshua Maurice
    Jan 21, 2011
  4. Johannes Schaub (litb)

    Is the aliasing rule symmetric?

    Johannes Schaub (litb), Jan 21, 2011, in forum: C++
    Replies:
    68
    Views:
    1,169
    Joshua Maurice
    Feb 6, 2011
  5. Xavier Roche
    Replies:
    3
    Views:
    113
    James Kuyper
    Mar 25, 2014
Loading...

Share This Page