[comp.lang.c] computing &arrnegative_offset

chris@mimsy.UUCP (Chris Torek) (09/17/88)

In article <16041@ism780c.isc.com> news@ism780c.isc.com (News system)
[really Marv Rubinstein in disguise] writes:
>But consider what might have happened had dpANS mandated that the compution
>of a pointer to x[-1] be a valid operation.  Then machines for which the
>mandated behavior is slow would be not used by people interested in high
>performance.  The net effect could be salubrious for the computer industry
>in the long run.

Perhaps.  I, for one, would find it useful to be Officially Allowed to
compute &arr[negative_offset].  I already make use of this (nonportably) in
existing code.  There is a second pitfall, however.

Consider the following (not strictly conforming) code:

	struct array_descriptor {
		int	low;		/* lower bound */
		int	bound;		/* upper bound - lower bound */
		int	*data;		/* pointer to &data[0] */
	};

	/*
	 * Allocate a new array whose subscripts range from [low..high)
	 */
	struct array_descriptor *new_array(int low, int high) {
		struct array_descriptor *p;
		int bound, *dp;

		/* first get a descriptor */
		if ((p = (struct array_descriptor *)malloc(sizeof(*p))) == NULL)
			return (NULL);

		/* then check for degenerate arrays (no data) */
		p->low = low;
		if ((bound = high - low) <= 0) {
			p->bound = 0;
			p->data = NULL;
		} else {
			/* allocate data */
			if ((dp = (int *)malloc(bound * sizeof(*dp))) == NULL) {
				free((char *)p);
				return (NULL);
			}
			p->bound = bound;
			p->data = &dp[-low];	/* virtual zero point */
		}
		return (p);
	}

If the computation `&dp[-low]' does not over- or under-flow, it
produces some pointer.  If `low' is positive, it produces a pointer
that does not point to valid data, but as long as that pointer is
used by adding a value in [low..high) before indirecting, things
should work out.

Now consider the free routine:

	void free_array(struct array descriptor *p) {

		if (p->data != NULL)
			free((char *)(&p->data[p->low]));
		free((char *)p);
	}

Do you see the hidden assumption here?

		if (p->data != NULL)

but p->data is not a `valid' pointer.  Maybe we had best write

		if (&p->data[p->low] != NULL)

but this is no good either (look again at new_array).  At least

		if (p->bound)

seems safe.  But what *really* happens if, in new_array, &p->data[-low]
turns out to `just happen' to equal NULL?

The approach I used in my own (nonportable) code was to keep the
original pointer around, just in case (and because there was no
p->bound available: allocation of data objects is deferred until they
are needed).  This also `just happens' to keep happy some garbage
collecting C runtime systems.  The above code, run on such a system,
might fail mysteriously after the garbage collector runs---because the
data pointer computed by &p->data[-100] is outside the region allocated
by malloc.  The GC routine would assume it was free, and cheerfully
release it for another malloc().
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris