From: mjs@hubcap.clemson.edu (M. J. Saltzman)
Subject: Re: Pointers outside of array boundary
Summary: Thanks and summary
Organization: Clemson University, Clemson SC
Date: Wed, 29 Jul 1992 18:38:19 GMT

Thanks to everyone who responded to my query about 1-based array indexing.
Below is a summary of replies.  The upshot is that there *is* a machine 
on which this code fails.

In article <1992Jul28.180945.22332@hubcap.clemson.edu> I wrote:
>I am having an e-mail conversation with someone regarding the
>_Numerical_Recipes_in_C_ practice of creating arrays with offset
>indices.  The sample code in question is 
>
>	float x = malloc(10*sizeof(float));
              ^
>	--x;

Should have been *x, of course (but it wasn't my code 8^)).

>which is supposed to give an array whose elements are addressed
>as x[1]..x[10].
>[...]
>     What could possibly go wrong if we only ever dereference 
>x[1]..x[10]?
>[...]
>My question is: Can anyone name an existing machine on which this code
>could fail, or give a really convincing hypothetical argument why it
>is a bad idea?
>[...]
>
>Also, could someone quote the relevant part of the standard?
>[...]

Mark Brader (msb@sq.com) quoted the standard, and the rationale:

}It's in section 3.3.6 (ANSI numbering, 6.3.6 in ISO numbering), which
}defines the operators + and -.  (The statement could have been written
}as x = x - 1; and this would be equivalent; I won't trouble to cite the
}parts of the standard that specify this equivalence.)  Here's the sentence:
}
}#  If both the pointer operand and the result point to elements of
}#  the same array object, or one past the last element of the array
}#  object, the evaluation shall not produce an overflow, otherwise
}#  the behavior is undefined.
}
}"Undefined behavior" allows the program to abort, or worse.

And:

}      The "one past the end of the array" rule wasn't in K&R, but people
}were in the habit of using it.  The Rationale says:
}
}| An important endorsement of widespread practice is the requirement
}| that a pointer can always be incremented to _just_past_ the end of
}| an array, with no fear of overflow or wraparound:
}| 
}| 	SOMETYPE array[SPAN];
}| 	/* ... */
}| 	for (p = &array[0]; p < &array[SPAN]; p++)
}| 
}| This stipulation merely requires that every object be followed by one
}| byte whose address is representable.  That byte can be the first byte
}| of the next object declared[,] for all but the last object located in
}| a contiguous segment of memory.  (In the example, the address &array[SPAN]
}| must address a byte following the highest element of array.)  Since the
}| pointer expression p+1 need not (and should not) be dereferenced, it is
}| unnecessary to leave room for a complete object of size sizeof(*p).
}| 
}| In the case of p-1, on the other hand, an entire object _would_ have
}| to be allocated prior to the array of objects that p traverses, so
}| decrement loops that run off the bottom of an array may fail.  This
}| restriction allows segmented architectures, for instance, to place
}| objects at the start of a range of addressable memory.
}
}They are tacitly assuming here that a pointer to a multi-byte object
}is implemented using a pointer to the first byte.  While the standard
}does not actually require this, it is common practice.  Since the Rationale
}is only suggesting a convenient implementation, that's all right.

Most people suggested hypothetical architectures on which performing
the decrement operation could result in a trap, due to either
underflow on an offset pointer or hardware-implemented range checking.
But Barry Gorman (GORMAN_B@prime1.lancashire-poly.ac.uk) reports:

}I have tried your code (slightly extended) on a Pr1me '50 series.
}This machine has a 'segmented' architecture, where a pointer has a segment
}field and an offset field (plus a bit more). If malloc() serves up a pointer
}with a zero offset (it does if you grab a full segment) then decrementing
}it causes the offset to go to 0xFFFE, and decrements the segment field.
}When you use an index such as x[2], only the offset field gets incremented,
}and the segment field is one short, i.e. it uses the WRONG segment.

So this is a case where the decrement and increment operators don't
behave as inverses, i.e. for float *x, x != --x + 1.  This seems to me
to be much more insidious than trapping the decrement operator.

Anyhow, I think that clears the matter up pretty completely.  Thanks
again to everyone who repiled.

-- 
		Matthew Saltzman
		Clemson University Math Sciences
		mjs@clemson.edu