C99 clarification on pointers to aggregate object and its sub objects



I have seen lengthy and inconclusive arguments on the use of pointers
to an aggregate object and its sub objects, so I would like to clarify
it here. The relevant sections of C99 are these:

6.3.2.3/7: 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.

6.5.3.2/3: The unary & operator yields the address of its operand. If
the operand has type ‘‘type’’,
the result has type ‘‘pointer to type’’

6.5.6/8: When an expression that has integer type is added to or
subtracted from a pointer, ..., If both the pointer operand and the
result point to elements of the same array object, or one past the
last element of the array object, the evaluation shall not produce an
overflow; otherwise, the
behavior is undefined.

6.5.6/9: When two pointers are subtracted, both shall point to
elements of the same array object,
or one past the last element of the array object

6.5.8/5: When two pointers are compared, the result depends on the
relative locations in the
address space of the objects pointed to. If two pointers to object
types both point to the
same object, or both point one past the last element of the same array
object, they
compare equal. If the objects pointed to are members of the same
aggregate object,
pointers to structure members declared later compare greater than
pointers to members
declared earlier in the structure, and pointers to array elements with
larger subscript
values compare greater than pointers to elements of the same array
with lower subscript
values. All pointers to members of the same union object compare
equal. If the
expression P points to an element of an array object and the
expression Q points to the
last element of the same array object, the pointer expression Q+1
compares greater than
P. In all other cases, the behavior is undefined.

6.5.9/6: Two pointers compare equal if and only if both are null
pointers, both are pointers to the
same object (including a pointer to an object and a subobject at its
beginning) or function,
both are pointers to one past the last element of the same array
object, or one is a pointer
to one past the end of one array object and the other is a pointer to
the start of a different
array object that happens to immediately follow the first array object
in the address
space.

Given that sizeof(int) == 4, and the declaration:
int a[3][4];
int *p = & a[0][0];
int (*q)[4] = &a [0];
int (*r)[3][4] = &a;

The questions are:
a) Is the conditional expression (p == r) always true? In others
words, is 6.5.9/6 transitive ?

b) Is (char *)p:
i) a pointer to the first byte of the integer object a[0][0] or
ii) a pointer to the first byte of the 1 dimensional array a [0] or
iii) a pointer to the first byte of the 2 dimensional array a or
iv) all of the above ?

c) Following (b), do any of these lead to undefined behaviour?
i) memset ((char*)p, 0, sizeof (*p));
ii) memset ((char*)p, 0, sizeof (*q));
iii) memset ((char*)p, 0, sizeof (*r));

d) Since an array in an expression is converted to a pointer to its
1st element, and &a[0][0] points to an object of integer size, are
these really valid:
i) memset (a, 0, sizeof (a));
ii) memset (&a[0][0], 0, sizeof (a));
or we can only do this:
iii) memset (&a, 0, sizeof (a));

e) If the pointer is not to the first element, is there any
difference? Is there undefined behaviour in this:
i) memset (&a[1][0], 0, sizeof (a) - sizeof (a[0]));
ii) memset (&a[1], 0, sizeof (a) - sizeof (a[0]));

f) When are 2 pointers, one derived from an object and another from
the subobject considered to 'point to the same object'. Do any of
these expressions lead to undefined behaviour?
i) (char *)&a[0][0] < (char *)a + sizeof (a)
ii) (char *)&a[0][1] < (char *)a + sizeof (a)
iii) (char *)&a[0][1] + 1 < (char *)&a[2][0]
.