From: volpe@bart.NoSubdomain.NoDomain (Christopher R Volpe)
Newsgroups: comp.lang.c
Subject: Re: Is this ANSI?
Date: 23 Nov 92 17:53:10 GMT
Reply-To: volpe@ausable.crd.ge.com
Organization: GE Corporate Research & Development

In article <24270@alice.att.com>, ark@alice.att.com (Andrew Koenig) writes:
|> 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.

I agree with Mark.

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

[stuff deleted]

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

p->data, as operand to the "[]" operator, decays into a pointer-to-char.
It points somewhere within the block returned by malloc().

|> definition of p, I see that p->data is an array of char with one element.

After it decays, it no longer has array type.

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

Both the operand and result point within the array-of-char returned by
malloc.

|> 
|> 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?

Of course not. This is a bogus argument. Consider this:
      int i[100];
      int *p, *q=i;
      p = malloc(100 * sizeof(int));

Now, if I do:
  
      *p = *q;

Is the compiler obligated to realize that p and q, which are declared as
"pointer to int", actually point to something other than single ints? 
Should it copy the entire array? Of course not. Does this mean that p[5]
is an illegal reference?? Of course not. Otherwise, to allocate dynamic
arrays of ints, one would have to do:

      typedef int array[100];
      array *p;
      p = (array *) malloc(sizeof(array));
      /* blah blah blah */
      (*p)[5] = whatever;


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

Yes, *p is of type FM, just as in my example, *p was of type int.
It's still not relevant. 

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


There's no family of related types here. The type of interest is "char *",
which is what p->data decays into.

|> 
|> 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?

It can diagnose it at runtime if it can determine that p->data and
p->data+5 don't point within the same object. If it tries to do this at
compile-time, file a bug report.

|> -- especially after the description of pointer arithmetic above?
|> -- 
|> 				--Andrew Koenig
|> 				  ark@europa.att.com

-- 
==================
Chris Volpe
G.E. Corporate R&D
volpecr@crd.ge.com
Newsgroups: comp.lang.c