From: fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON)
Subject: Struct hack (was: Re: Is this ANSI?)
Followup-To: comp.std.c
Organization: Computer Science, University of Melbourne, Australia
Date: Mon, 23 Nov 1992 15:48:23 GMT

ark@alice.att.com (Andrew Koenig) writes:

>... to convince me
>that it's legal, you'd have to find language in the standard that allows
>it.

Sure, but to convince me that it's illegal, you'd have to find language
in the standard that disallows it :-)

>I looked around a little bit, and here is what I found, in the description
>of pointer+integer:
>
>	Unless both the pointer operand and the result point to
>	elements of the same array object, or the pointer
>	operand points one past the last element of an array
>	object and the result points to an element of the same
>	array object, the behavior is undefined if the result
>	is used as an operand of the unary * operator.

The way I read it, this sentence is implicitly existentially qualified
with respect to the array object. That is, it means "Unless there
exists some array object which is pointed to by both the pointer
operand and the result, ...".  This reading is supported
by the use of the phrase "the last element of *an* array object".

>Now, let's take a look at the example again:
[example omitted]
>At issue is the assignment to p->data[5].
>
>As I see it, the question is just what p->data is.  If I look at the
>definition of p, I see that p->data is an array of char with one element.
>The way I've used it is, of course, equivalent to *(p->data+5).
>
>That means p->data is converted to a pointer to its initial element,
>namely a pointer to the first (and only) element of a one-element array.
>[Remember this assertion].

This is not the *only* thing that p->data points to.

>So, we take that pointer, add 5 to it,
>and apply unary * to the result.  Well, the pointer operand of +
>points to an element of the array object, but the result does not;
>there is only one element and the result doesn't point there.
>The result of using unary * is therefore undefined.

No, because *there exists* an array object for which
both the pointer operand and the result point to elements of that
array object.

>Now, look back at the assertion I asked you to remember.  I expect
>that Mark Brader is going to say `But p->data isn't an array of
>one element, it's an array of 101 elements.  After all, when you
>said malloc(sizeof(struct FM + 100), you allocated enough memory
>for 101 elements!'
>
>Here are two reasons I disagree:
>
>	1. Suppose I said
>
>		struct FM *q = (struct FM *) malloc(sizeof(struct FM) + 100);
>
>	and later said
>
>		*p = *q;
>
>	Is the compiler obligated to realize that p and q actually point
>	to objects of type other than struct FM?  That is, is it obligated
>	to copy all the `extra' elements in the data array?

No; but I don't see how this is directly relevant.

>	2. If you declare p by saying
>
>		struct FM *p;
>
>	then I don't think there is any place in the standard that says
>	other than that *p is an lvalue of type FM.  Once you acknowledge
>	that *p is of type FM, I don't see how you can get away from the
>	p->data being anything other than a one-element array.

As I see it, the question is not what is the type of p->data, the question is
rather what does the pointer (char *)p->data point to, and the answer is
that it points to a one-element array and *also* points into a larger array.

>	What I found
>	in the standard that talks about this is:
>
>		The pointer returned [by malloc or realloc] if the
>		allocation succeeds is suitably aligned so that it
>		may be assigned to a pointer to any type of object
>		and then used to access such an object or an array
>		of such objects in the space allocated (until the
>		space is explicitly freed or reallocated).
>	
>	It doesn't say that it may be assigned to a pointer to some
>	type of object and then used to access an object of some other
>	type.

No, but it doesn't say that this is *not* allowed, either.

>Another way to look at it is this.  Suppose I have a compiler that rejects:
>
>	void f(struct FM *p)
>	{
>		p->data[5] = '?';
>	}
>
>on the basis that p->data is a one-element array and you can't refer to
>the fifth element of a one-element array.  What part of the standard can
>you cite to convince me that my compiler isn't allowed to diagnose this?
>-- especially after the description of pointer arithmetic above?

As I said before, the fact that p->data is a one-element array doesn't
imply that (char *)p->data does not also point to some other array which does
have a element at the position *(p->data+5), and if it does, the
statement p->data[5] = '?' should be quite legal. Obviously the compiler
can't reject a program because it *might* contain a statement whose behaviour
is undefined. (By the way: exactly when *is* a compiler allowed to reject
a program?)

Clearly the statement is syntactically correct.
I believe that the onus is on those who believe the statement to be illegal to
cite supporting evidence, and I do not believe that what you have just
cited is convincing.

P.S. I haven't read the standard myself and I could well be wrong :-)
     I am just arguing a point of view, to see what the response
     from supporters of the opposing point of view will be.
     
P.P.S. Followups to comp.std.c.

-- 
Fergus Henderson             fjh@munta.cs.mu.OZ.AU      
This .signature virus is a self-referential statement that is true - but 
you will only be able to consistently believe it if you copy it to your own
.signature file!
Newsgroups: comp.lang.c