Some additional thoughts about this:
> Andrey:
> > >>> int arr[100];
> > >>> int (*parr)[5] = (int(*)[5]) &arr;
> > >>>
> > >>> (*parr)[5] = 42;
Note that if this would not be permitted,
int *qarr = (int *)&arr;
qarr[5] would also not be permitted.
the only difference is that parr is a pointer to array 5 of int, and qarr
is a pointer to int (which, when indexing, would be assumed to be an array 1
of int). So when indexing the actual *type* of the pointer is not relevant.
This does *not* destroy the possibility of bounds checking. When an object
is created, remember first address and size of the object and transfer that
to every pointer pointing into the object (or one past the object).
I see however one problem. Consider:
typedef struct {int p, q;} structure;
structure x;
is:
(&(x.p))[1]
correct? You would not like that, but it is nevertheless allowed
(arithmetic is still within the main object). Unless you consider that
x.p denotes a member object (of type int), and use that in the description
of valid indexing. But if you do that you get in problems with:
int a[5];
(&(a[3]))[1];
because now a[3] denotes an element object, and this indexing would also
not be allowed (the element object is also of type int).
The only solution (in my opinion) is to consider member objects (of structs
and unions) as true objects, while element objects are not so considered,
in the context of indexing.