Ivan A. Kosarev said:
As you know, in the abstract machine the elements of an array object are
never accessed through this array, so there is no need to involve the terms
of compatible objects.
I'm afraid that I don't know that. The phrase "through this array"
seems odd to me, and I suppose you could define what it means in a way
that makes that statement true, but I don't see it as being true in
any sense that is relevant to this discussion.
Yes, "int a[2][2][2][2]" and "int b[16]" are not compatible types, but the
integer elements of the objects are accessed in the same way - through
calculation of a value of type "int*" which can be dereferenced to get a
lvalue of corresponding element. Since the value is calculated, it's not
important a part of which object the element is and what type the object
has.
I agree that on most real implementations, possible all of them, this
is perfectly true. However, I'm talking about what the standard
requires/allows, not what's actually been implemented. The wording of
the standard allows the access to be performed in a more complicated
fashion than the one you describe. Since the expression a[0][0][0][3]
has undefined behavior, an implemention is free to implement the
access in a way that involves a validity check, with a different valid
range for the check than would apply to b[3].
....
Exactly. And the validity limits are defined (e.g., C99 6.5.8) in terms of
the elements of an array *object*. This mean that even for "int
a[2][2][2][2]" all the integers can be pointed, and all the pointers can be
correctly compared.
What matters here are the limits "When an expression that has integer
type is added to or subtracted from a pointer". Those limits are
described in 6.5.6p8, not 6.5.8. They are defined in terms of elements
of the applicable array object. There's an array object whose elements
are a[0] and a[1]. There's an array object identified by a[0][1][0]
whose elements are two integers. However, there's no array object
whose element list contains all of the ints that are elements of
elements of elements of a.
....
a[1]+2 points one past the end of the array object a[1], which happens to
be at the same location as the position one past the array object a. 'b'
points at the first element of the array object a[0]. It happens to point
at the same memory location as the start of the array object 'a', but it
has the wrong type to point at the first element of 'a'. Therefore,
the
What's wrong with the type?
It's an int*, which means that the valid range of offsets to a
pariticular pointer of that type can only be set by the size of an
object of type 'array of int', or by an object of type int (which is
treated like an array of length 1). An array object whose element type
is 'array of int' doesn't qualify.
limits on what values can legally be added to 'b' are determined by the
number of elements in a[0], not the total number of elements of
elements
As I said before, it's not important what is a *type* of "a[0]". The limits
are determined for *objects*.
Yes, and the relevant limits for any pointer derived from the pointer
that a[0] decays into are determined by the size of the array object
a[0].
They are not determined by the size of the array object 'a', because
the elements of 'a' aren't ints; as a result the rules in 6.5.6 would
be absurd, if they did apply. They say that "if the expression P
points to the i-th element of an array object, the [expression] (P)+N
.... [points at] ... the i+n-th [element] of the array object ...". If
'a' were the relevant array, that statement would have the absurd
result of saying that a[0][0][0]+3 has a value which points at a[3],
since i==0 and N==3.
Again, the array *object* has exactly four integers.
No, it does not. It has two array objects as its elements. The
relevant array object only has two integers as its elements, and it's
therefore not legal to add 3 to a pointer to one of its elements.
therefore there isn't any pointer here for which it would be legal to add
3 to it. Therefore, b+4 isn't a legal expression, and it's
meaningless to
That cannot be true.
a[1] + 2 is exactly the same as
(int*) ((int(*)[2]) a + 1) + 2,
Agreed.
((int*) a + 2) + 2,
Numerically, they're the same, but the validity limits for offsets to
the pointer that results from the (int*) cast in the first expression
are different from the validity limits for offsets to the result of
the (int*) cast in the second one. As a result, an implementation may
legally implement the second one in such a way that the program aborts
when the final 2 is added, something that wasn't true for the first
expression.
That is what should happen in the abstract machine. Do you see other ways?
Since the behavior is undefined, the standard imposes no behavior on
the abstract machine, just as it imposes no behavior on a real
implementation.