From: ark@alice.att.com (Andrew Koenig)
Newsgroups: comp.lang.c
Subject: Re: Is this ANSI?
Date: 22 Nov 92 14:32:08 GMT
Article-I.D.: alice.24270
Reply-To: ark@alice.UUCP ()
Organization: AT&T Bell Laboratories, Murray Hill NJ

In article <1992Nov21.125038.26244@sq.sq.com> msb@sq.sq.com (Mark Brader) writes:

> Andrew, I'm afraid that *you* are definitely wrong, period.

After thinking it over carefully, I'm not convinced.

> The key word here is "allocate", meaning that the intention is to
> do something like

> 	struct FM *p = malloc (sizeof (struct FM) + n);

Agreed.

> In this situation the standard *does* allow a correctly declared
> array element of the struct to be subscripted out of range, as long
> as you stay within the malloc()ed area.  This has been discussed
> at great length in comp.std.c not too long ago.  The only argument
> that it might be illegal in this situation which was not countered
> by reference to the standard amounted to saying that they must
> have *intended* it to be illegal and didn't get the wording right.

Unfortunately, I wasn't in on that discussion.  But to convince me
that it's legal, you'd have to find language in the standard that allows
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.

Now, let's take a look at the example again:

	struct FM {
		int size;
		char data[1];
	};

	main()
	{
		struct FM *p = (struct FM *) malloc(sizeof(struct FM) + 100);
		p->data[5] = '?';
	}

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].  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.

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?

	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.  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.  That is, it says that the result of malloc may be
	assigned to a struct FM * and then used to access an object
	of type struct FM.  It says nothing about struct FM representing
	a family of related types.

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?
-- 
				--Andrew Koenig
				  ark@europa.att.com
Newsgroups: comp.lang.c