1

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? This question also applies to C.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • Could you please add some code illustrating what you mean by "the opposite direction"? What code do you think might not be allowed? – Rob Kennedy Jan 21 '11 at 14:05
  • @Rob I thought that I showed. Maybe I failed. `a = 0` might not be allowed, as well as `*ra = 0`. With "Opposite direction" of "A foo containing bar" I mean "A bar contained by foo". What is unclear in particular? – Johannes Schaub - litb Jan 21 '11 at 14:29
  • If you mean, `char a='A'; int *pi = (int*)a; *pi='B';`, then I don't think such thing is safe to begin with. – Nawaz Jan 21 '11 at 15:02

3 Answers3

2

The aliasing rule simply states that there's one "effective type" (C99 6.5.7, plus footnote 73) for any given object in memory, and any accesses to such an object go through one of:

  • A type compatible with the effective type (qualifiers such as const and restrict, as well signed/unsigned-ness may vary)
  • A struct or union containing one such type
  • A character type

The effective type isn't specified in advanced, of course - it's just a construct that is used to specify aliasing. But the intent is simply that you don't access the same object with two different non-character types.

So the answer is, yes, you can indeed go the other direction.

bdonlan
  • 224,562
  • 31
  • 268
  • 324
  • It's a rather bold move to claim that the C99 standard *intended* to say something, without any reference to back up this claim, and then draw conclusions what is legal in C. The C99 standard is pretty clear there, and there is no evidence for your claim. – user2719058 Dec 04 '13 at 19:04
1

The standard (C99 6.3.2.3 §7) defines pointer casts like this as "just fine", the casted pointer will point at the same address. (Unless the CPU has alignment which makes the cast impossible, then it is undefined behavior.)

That is, the actual cast in itself is fine. What will happen if you start to manipulate the data... now that's another implementation-defined story.

Here's from the standard:

"A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. 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.

When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object."

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

Lundin
  • 195,001
  • 40
  • 254
  • 396
0

I think I'm a bit confused by the question, but the 2nd and 3rd examples are accessing the int through an lvalue that has the object's type (int in the examples).

C++ 3.10/15 states as it's first item that it's OK to access the object through an lvalue that has the the type of "the dynamic type of the object".

What am I misunderstanding in the question?

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • I mean, in `int a[1];` when we do `a[0] = 0;` we do change the stored value of two objects. The first one has type `int`, and the second one has type `int[1]`. As another example, in `struct A { int a; };` when we do `A a; a.a = 0;` we change the stored value of two objects. The first object is of type `int` and the second object is of type `A`. There exist aliasing rules allowing us to do this in the opposite direction (changing the element or member by the aggregate lvalue is allowed), but not in this direction. That's what I mean to ask. – Johannes Schaub - litb Jan 21 '11 at 22:52
  • @Johannes: I think I understand the question now (but still not entirely sure). But, I still see no ambiguity in the standard. `a[0] = 0` (or `a.a = 0` is accessing the lvalue through the dynamic type (`int`) so it's well-defined behavior. A subsequent access that goes through the `int[1]` (or `struct A`) type would have to reflect that updated value (due to the standard's abstract machine definition). – Michael Burr Jan 21 '11 at 23:07