jacob navia said:
Elsewhere in this thread it has already been explained, and demonstrated in
at least two separate ways, why the above claim is incorrect.
C's array model, however, requires that each object with an
address (i.e., each non-"register" object whose address is
taken at some point) *appear* to work that way.
The peculiar thing about this is that the "integer part", as it
were, is not necessarily unique, and in fact is usually non-unique
if you access it in the portable way:
T1 *p1, array1[N];
T2 *p2, array2[N];
size_t o1, o2;
p1 = array1; /* p1 is, in effect, an <array1, offset1> pair */
p2 = array2; /* p2 is, in effect, an <array2, offset2> pair */
... code that adjusts p1 and p2, e.g., *p1++ and the like ...
o1 = p1 - array1; /* finds <offset1> */
o2 = p2 - array2; /* finds <offset2> */
It is not at all unusual for o1 and o2 to be equal; in fact, it is
usually the case that o1 and/or o2 are equal to some other integer
computed from some other pointer by subtracting that pointer's
base.
The values of offset1 and offset2 are integral, and initially both
are 0 (because p1==&array1[0] and p2==&array2[0]). Each "p1++" or
"p2++" increments the corresponding offset, giving rise to the
"consecutive integers" Jacob Navia described.
Interestingly, C's bytes-of-object model -- in which the address
of any object can be converted to "unsigned char *" and the
object's underlying byte-at-a-time representation extracted --
allows one to break up these whole integers into "fractional
integers", in a sense. If, e.g., sizeof(T1) is 4, then when
offset1 is 5 (so that p1==&array1[5]), if we do:
unsigned char *q0, *q1;
size_t x;
q0 = (unsigned char *)&array[0];
q1 = (unsigned char *)p1;
x = q1 - q0;
we are guaranteed to get x == 20. More generally, here, if we
compute o1 as above, we get x == o1 * sizeof(*p1).
On machines like the 64-bit-word but 8-bit-"char" Cray, this
requires that pointer arithmetic compile to some extra-fancy
machine code, in some cases. In particular, q1 - q0 is not
just plain subtraction. The machine code for computing x is
effectively the same as that for:
x = ((int)q1 & 0xffffffffffff) - ((int)q0 & 0xffffffffffff) * 8
+ (((int)q1 >> 48) - ((int)q0 >> 48));
(On more conventional machines, computing o1 and o2 from p1 and p2
implies a division step, where the result of the division never
has a remainder. This allows compilers to use shortcuts in most
cases on most machines. I believe I was the one who convinced RMS
to put in that feature internally, when I pointed out that an early
gcc was generating a full-blown divide for pointer subtraction.)