On 1/10/2012 01:59, ty ty wrote:
> 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:
>
> [...references...]
>
> Given that sizeof(int) == 4, and the declaration:
> int a[3][4];
> int *p =& a[0][0];
(Which is equivalent to 'int * p = a[0] + 0;')
> int (*q)[4] =&a [0];
(Which is equivalent to '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 ?
>
Never true, as noted else-thread.
> 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 ?
>
(i): 'p' was initialized with the pointer value of '&a[0][0]', which is
like any of these:
int * p = &(a[0][0]);
int * p = &( (*(a + 0)) [0] );
int * p = &( (*a) [0] );
int * p = &( *((*a) + 0) );
int * p = &( *((*a)) );
int * p = &( *(*a) );
int * p = &( **a );
int * p = *a;
In the context of the last line just above, 'a' is converted to an
expression that has type 'int(*)[4]'. Application of the unary
indirection operator thus yields an expression with type 'int[4]'. That
expression is then converted to an expression with type 'int *'. The
pointed-to object is thus an 'int', which is your (i), above.
As luck would have it, '(char *) p' _also_ points to an element of an
array object with 'sizeof *p' elements of type 'char'.
> 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));
>
Yes. (ii) and (iii) do, since the function will attempt to write beyond
the bounds of the 'sizeof *p' elements of type 'char' that '(char *) p'
points into.
> 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));
Not valid. In that context, the first appearance of 'a' is converted to
an expression with type 'int (*)[4]', which is a pointer into a space
with size 'sizeof (int[4])' bytes. The second appearance of 'a' has
type 'int[3][4]' whose size is 'sizeof (int[3][4])', which is clearly more.
> ii) memset (&a[0][0], 0, sizeof (a));
Not valid. That pointer points to a single 'int' whose size is 'sizeof
(int)' but the size that was passed is 'sizeof (int[3][4])', which is
clearly more.
> or we can only do this:
> iii) memset (&a, 0, sizeof (a));
>
You can only do that. The pointer points to an 'int[3][4]' whose size
is 'sizeof (int[3][4])', which matches the passed size.
> 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]));
Yes. That pointer points to a single 'int' whose size is 'sizeof (int)'
but the size that was passed is 'sizeof (int[3][4]) - sizeof (int[4])',
which is clearly more.
> ii) memset (&a[1], 0, sizeof (a) - sizeof (a[0]));
>
Yes. That pointer points to an 'int[4]' whose size is 'sizeof (int[4])'
but the size that was passed is 'sizeof (int[3][4]) - sizeof (int[4]),
which is clearly more (twice the size).
> 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)
Yes. Before you can even do the comparison, '(char *) a + sizeof a'
yields undefined behaviour. '(char *) a' will point to both the first
byte of an 'int[4]' as well as to the first element of a 'char[sizeof
(int[4])]', but you attempt pointer arithmetic beyond that, to 'sizeof
(int[3][4])'.
> ii) (char *)&a[0][1]< (char *)a + sizeof (a)
Yes, for the same reason as (i).
> iii) (char *)&a[0][1] + 1< (char *)&a[2][0]
Yes. The first 'char *' pointer points to the first byte of an 'int' as
well as to the first element of a 'char[sizeof (int[4])]'. The second
'char *' pointer points to the first byte of a different 'int' as well
as to the first element of a different 'char[sizeof (int[4])]'. Since
these are pointing into different objects, the behaviour is undefined.
These might seem silly, but they don't hinder such things as:
int a[3][4];
int * p = *a;
p = p + 1;
Since the pointer value resulting from '*a' _does_ point to an 'int'
element of an 'int[4]'.
They do hinder:
int a[3][4];
int * p = *a;
p = p + 5;
But you could do the more tedious:
int a[3][4];
/* Point to "the whole space" first */
int * p = (void *) &a;
p = p + 5;
In that case, we know that we are pointing into the equivalent of an
'int[12]', since array elements have no padding between them.
Ugh. :(
- Shao Miller