Why pointers can only be subtracted ?

E

Eric Sosman

Richard said:
If I take a tape measure and put the end at some arbitrary distance
away, and measure the position of two points (along the line of the
measure) with it, then I can find the average position by adding the
measurements and dividing by two, and I will get the same answer
regardless of where the end of the measure is. Similarly, it works
perfectly well to add two temperatures in Fahrenheit and divide by two
to get the average, and it also works perfectly well in Celsius and
Kelvin, and you get the same answer whichever you use.

If a scale has some kind of zero offset, then of course the sum of two
values will have two of those offsets in it, but provided the meaning
you ascribe to the sum requires you to subtract off a value of the
same kind, or divide by two, before interpreting it on the original
scale, it will all cancel out.

The sum of two pointers doesn't make sense *as a pointer* any more
than the sum of two Fahrenheit temperatures makes sense as a
Fahrenheit temperature. But that doesn't mean it doesn't make sense
at all.

In an object-oriented language you could define a type "sum of two
pointers", whose constructor takes two pointers. Then you could
defined methods on the type such as subtraction of a pointer, which
would return a pointer.

Ah, I now see where you're heading. The choice of a zero
still presents a problem, though, because of possible overflow.
The "sum of pointers" object would need to be wider than the
summed pointers themselves, else the averaging you mention could
fail spectacularly (add two pointers to an object "near the top
of memory," lose the high-order bit of the sum to overflow, divide
by two, and you've got a pointer to someplace in "the bottom half
of memory," not even close to the original target object).

Another problem: If a "sum of two pointers" data type were
defined, somebody would come along and ask for a "sum of N
pointers" object, whose characteristics would be unlike those of
the two-pointer sum (e.g., dividing by 2 gives garbage, while
dividing by N makes sense; you must subtract N-1 pointers from
it rather than just one to retrieve an ordinary pointer, etc.).
And *then* someone will start asking how to calculate the standard
deviation of a population of pointer values ...

It might be possible to cook up such an extension, but I
think it would be, er, in poor taste.
 
R

Richard Tobin

Peter Nilsson said:
Temperatures are open ended scales. The sum will remain within infinite
range. This may not apply to pointer arithmetic where adding two
pointer values need not produce a value within an address space.

As I said, the sum of two pointers would not be a pointer. So the
question of whether it fits in the address space does not apply.
Also, your average can generally be performed with simple pointer
difference...

avg = p + (q - p) / 2;

Of course, but I am not arguing that it's a good thing, I'm just
refuting the claim that it's "meaningless".
But C pointers (addresses) need not be 'scale' values.

If I understand what aspect of "scale" you are referring to, then
within an object they have to be, for pointer subtraction to work.
Addition of pointers into different objects is no more or less
problematic than subtraction.
Indeed, there are multiple possible interpretations that pointer
addition _could_ have.

Bingo! You agree with me. There are multiple interpretations, not
none. The claim that you can't add pointers because "it's
meaningless" is wrong. The real reason you can't add pointers is
because it's not useful enough.
Do you think you can't do this with C?

I chose to describe it in object-oriented terms in the hope of making
it clearer.

-- Richard
 
C

Clark S. Cox III

Eric Sosman said:
AnyType array[2];
assert(&array[1] - &array[0] == 1);

... even if `sizeof(AnyType)' is 42, or 97, or 4191.

Did you test that theory? :)


The difference between &array[1] and &array[0] is
sizeof(AnyType)

You may want to test that yourself:

/*begin code*/
#include <stdio.h>

int main()
{
int array[2];

printf("%zd\n", &array[1] - &array[0]);
return 0;
}
/*end code*/
 
E

Eric Sosman

Mike said:
Mike said:
[...]

But We can subtract two pointers. Why ?

The difference between two pointers gives the size
(in bytes) of the portion of memory whose address
begins with the smaller and ends with the larger.

The difference is in units of the size of the pointed-
to type, not necessarily in bytes.


Are you sure? :)

Yes. It's a basic property of C's pointer arithmetic.
Remember we're talking about the
difference between two pointers, not the result of
subtracting an integer value from a pointer.

AnyType array[2];
assert(&array[1] - &array[0] == 1);

... even if `sizeof(AnyType)' is 42, or 97, or 4191.

Did you test that theory? :)

No. Nor have I double-checked that two plus two
still make four; it's a "theory" I've known and used for
so long that I no longer bother to verify it.
The difference between &array[1] and &array[0] is
sizeof(AnyType)

Have *you* tested *that* theory?

Pointers' behavior with respect to arithmetic is one
of the distinctions between "pointer" and mere "address."
Pointers with "the same values" (meaning that they point
to "the same address") can nonetheless be quite different,
because a pointer takes into account the type of its target
while a "bare machine address" has no notion of type.
 
R

Richard Bos

It's easy to assign reasonable meanings to it. For example, it's
something that you can divide by two to find the "average" address
(the midpoint of an array, for example). Or you can alternate between
the two addresses by subtracting the current one from it.

Neither of those provides a meaning for the sum itself - they just make
it slightly useful as an intermediate step in a computation.

Richard
 
K

Keith Thompson

Eric Sosman said:
Pointers' behavior with respect to arithmetic is one
of the distinctions between "pointer" and mere "address."
Pointers with "the same values" (meaning that they point
to "the same address") can nonetheless be quite different,
because a pointer takes into account the type of its target
while a "bare machine address" has no notion of type.

I understand the distinction you're making, but the standard uses the
term "address" as a near-synonym for "pointer". (See the tedious 'Why
do people call addresses "pointers"?' thread for some arguments on
that topic.) For example, the unary "&" operator yields the address
of its operand; this is a value of type pointer-to-whatever, not a
"bare machine address".
 
M

Mike Wahler

Eric Sosman said:
Mike said:
AnyType array[2];
assert(&array[1] - &array[0] == 1);
The difference between &array[1] and &array[0] is
sizeof(AnyType)

Have *you* tested *that* theory?

Pointers' behavior with respect to arithmetic is one
of the distinctions between "pointer" and mere "address."
Pointers with "the same values" (meaning that they point
to "the same address") can nonetheless be quite different,
because a pointer takes into account the type of its target
while a "bare machine address" has no notion of type.

I believe we were considering different contexts.

Here are my results with VC++ on Windows/Intel:

sizeof(int) == 4
&array[0] == 006CFDF0
&array[1] == 006CFDF4
(unsigned long)(&array[1] - &array[0]) == 1
(unsigned long(&array[1]) - (unsigned long)&array[0] == 4

But yes, you're right, pure 'pointer arithmetic' yeilds
a difference measured in units of object size. And the result
of conversion of pointers to integers is implementation
defined, so the above (the 'byte count' result) won't
necessarily hold true in all environments.

Sorry for being so 'quick on the draw'. :)


-Mike
 
K

Keith Thompson

Mike Wahler said:
Eric Sosman said:
Mike Wahler wrote: [...]
The difference between &array[1] and &array[0] is
sizeof(AnyType)
[...]
I believe we were considering different contexts.

Here are my results with VC++ on Windows/Intel:

sizeof(int) == 4
&array[0] == 006CFDF0
&array[1] == 006CFDF4
(unsigned long)(&array[1] - &array[0]) == 1
(unsigned long(&array[1]) - (unsigned long)&array[0] == 4

That's an odd interpretation of the "difference between &array[1] and
&array[0]".
 
E

Eric Sosman

Mike said:
Here are my results with VC++ on Windows/Intel:

sizeof(int) == 4
&array[0] == 006CFDF0
&array[1] == 006CFDF4
(unsigned long)(&array[1] - &array[0]) == 1
(unsigned long(&array[1]) - (unsigned long)&array[0] == 4

There's something screwy about the parentheses in
the final line ... Besides, as Keith Thompson points
out, even the corrected final line demonstrates nothing
about pointer subtraction, because it doesn't subtract
any pointers!
 
C

CBFalconer

Keith said:
Mike Wahler said:
Mike Wahler wrote: [...]
The difference between &array[1] and &array[0] is
sizeof(AnyType)
[...]
I believe we were considering different contexts.

Here are my results with VC++ on Windows/Intel:

sizeof(int) == 4
&array[0] == 006CFDF0
&array[1] == 006CFDF4
(unsigned long)(&array[1] - &array[0]) == 1
(unsigned long(&array[1]) - (unsigned long)&array[0] == 4

That's an odd interpretation of the "difference between &array[1]
and &array[0]".

Seems accurate to me. It's the last line that is peculiar.
 
K

Keith Thompson

CBFalconer said:
Keith said:
Mike Wahler said:
Mike Wahler wrote: [...]
The difference between &array[1] and &array[0] is
sizeof(AnyType) [...]
I believe we were considering different contexts.

Here are my results with VC++ on Windows/Intel:

sizeof(int) == 4
&array[0] == 006CFDF0
&array[1] == 006CFDF4
(unsigned long)(&array[1] - &array[0]) == 1
(unsigned long(&array[1]) - (unsigned long)&array[0] == 4

That's an odd interpretation of the "difference between &array[1]
and &array[0]".

Seems accurate to me. It's the last line that is peculiar.

I think we're now officially beating a dead horse, but I think of the
"difference between &array[1] and &array[0]" as the result of the
expression

&array[1] - &array[0]

not the difference between their values cast to unsigned long.

Similarly, the difference between 3.5 and 2.0 is 1.5, not 1 (the
result of (unsigned long)3.5 - (unsigned long)2.0).
 
M

Mike Wahler

Keith Thompson said:
Mike Wahler said:
Eric Sosman said:
Mike Wahler wrote: [...]
The difference between &array[1] and &array[0] is
sizeof(AnyType)
[...]
I believe we were considering different contexts.

Here are my results with VC++ on Windows/Intel:

sizeof(int) == 4
&array[0] == 006CFDF0
&array[1] == 006CFDF4
(unsigned long)(&array[1] - &array[0]) == 1
(unsigned long(&array[1]) - (unsigned long)&array[0] == 4

That's an odd interpretation of the "difference between &array[1] and
&array[0]".

The fourth line gives the difference measured in object size.
The fifth line gives the difference measured in bytes.

But I won't deny that my thoughts are sometimes odd. :)

-Mike
 
M

Mike Wahler

Eric Sosman said:
Mike said:
Here are my results with VC++ on Windows/Intel:

sizeof(int) == 4
&array[0] == 006CFDF0
&array[1] == 006CFDF4
(unsigned long)(&array[1] - &array[0]) == 1
(unsigned long(&array[1]) - (unsigned long)&array[0] == 4

There's something screwy about the parentheses in
the final line ...

Yes, I see that now, but that's just a mistake in the
literal I passed to 'printf'(). The '%lu' argument
passed to printf() was:

(unsigned long)&array[1] - (unsigned long)&array[0])

I.e. each address was converted to an integer, then one
subtracted from the other.
Besides, as Keith Thompson points
out, even the corrected final line demonstrates nothing
about pointer subtraction, because it doesn't subtract
any pointers!

Sure it does. It subtracts the address of array[0]
from the address of array[1].

(Unfortunately I've already thrown the code away.)

I seem to have committed so many blunders here lately,
I think maybe I should simply lurk for a while. :)

-Mike
 
C

Chris Croughton

Eric Sosman said:
Besides, as Keith Thompson points
out, even the corrected final line demonstrates nothing
about pointer subtraction, because it doesn't subtract
any pointers!

Sure it does. It subtracts the address of array[0]
from the address of array[1].

No, it subtracts two long integers which have some value resulting from
the conversion of pointers to long int (non-portably, since the standard
doesn't say what the result of that conversion is), it doesn't subtract
the pointers.

You may have illustrated the difference between pointers and addresses
(on your machine and compiler), but it's undefined what happens when you
cast a pointer to a long int. For instance, on a word-addressed machine
you could get something like:

int array[2];
&array[1] - &array[0] == 1 /* defined by the standard */
(long)&array[1] - (long)&array[0] == 1
(long)(char*)&array[1] - (long)(char*)&array[0] == 4
sizeof(char) == 4

(a pointer to long being stored as a word address and a pointer to char
being stored as a byte address). Or all sorts of other undefined
behaviour. It is only defined that if you have pointers to an array
then the difference of the pointers is the number of array elements (if
the pointers don't point to the same array the difference is undefined
behaviour).
(Unfortunately I've already thrown the code away.)

That seems wise <g>...

Chris C
 
P

Peter Nilsson

Richard said:
As I said, the sum of two pointers would not be a pointer. ...

Prior to my comment, you were talking about adding two temporatures
to produce a temperature. I took your temperatures as similies
for pointers, but no matter.
Of course, but I am not arguing that it's a good thing, I'm just
refuting the claim that it's "meaningless".


If I understand what aspect of "scale" you are referring to, then
within an object they have to be, for pointer subtraction to work.

What I meant was, they are not scale values in the general sense.
In other words, a given pointer is not necessarily an offset from
address 0 (whatever that might mean.)
Addition of pointers into different objects is no more or less
problematic than subtraction.

I don't understand how your example of adding pointers can work
without the 'global scale' assumption about pointers.

Eric's comments notwithstanding...

If you think of (p + q) as ((p - a) + (q - a)), where 'a'
is the pointer to the first element, I can't see how an
implementation can determine 'a' implicitly in all cases.
Bingo! You agree with me. There are multiple interpretations, not
none. The claim that you can't add pointers because "it's
meaningless" is wrong. The real reason you can't add pointers is
because it's not useful enough.

More so because whichever one the Committee chose would invariably
be viewed as 'unnatural' by most programmers. I'm sure what Eric
means is that there is no obvious natural meaning to pointer
arithmetic.
 
R

Richard Bos

Mike Wahler said:
Keith Thompson said:
Mike Wahler said:
Here are my results with VC++ on Windows/Intel:

sizeof(int) == 4
&array[0] == 006CFDF0
&array[1] == 006CFDF4
(unsigned long)(&array[1] - &array[0]) == 1
(unsigned long(&array[1]) - (unsigned long)&array[0] == 4

That's an odd interpretation of the "difference between &array[1] and
&array[0]".

The fourth line gives the difference measured in object size.
The fifth line gives the difference measured in bytes.

Not necessarily. Change the casts to (char *), then you're guaranteed
the expected result.

Richard
 
C

CBFalconer

Mike said:
.... snip ...

Yes, I see that now, but that's just a mistake in the
literal I passed to 'printf'(). The '%lu' argument
passed to printf() was:

(unsigned long)&array[1] - (unsigned long)&array[0])

I.e. each address was converted to an integer, then one
subtracted from the other.
Besides, as Keith Thompson points
out, even the corrected final line demonstrates nothing
about pointer subtraction, because it doesn't subtract
any pointers!

Sure it does. It subtracts the address of array[0]
from the address of array[1].

No it doesn't. It subtracts integers, not addresses, because of
the (suspicious) casts, whose effect is already implementation
dependant.
 
J

Joona I Palaste

Chris Croughton said:
infobahn said:
Consider the addresses 1562 Ram Street and 1838 Ram Street.
[...]

But if we subtract one from the other, we get 276 - the number
of houses you'd have to walk past to get from one to the other
(including one of the end points). This turns out to be a
useful number.

You must live in a place with a different approach to addresses.
By the e-address, infobahn is in Britain, where adresses di often work
like that (although more of them are word addresses, incrementing by two
with odd bytes -- er, houses -- on one side and even ones on the other).

You mean it doesn't work like that everywhere?
 
M

Michael Mair

Joona said:
Chris Croughton said:
Consider the addresses 1562 Ram Street and 1838 Ram Street.

[...]


But if we subtract one from the other, we get 276 - the number
of houses you'd have to walk past to get from one to the other
(including one of the end points). This turns out to be a
useful number.

You must live in a place with a different approach to addresses.
By the e-address, infobahn is in Britain, where adresses di often work
like that (although more of them are word addresses, incrementing by two
with odd bytes -- er, houses -- on one side and even ones on the other).

You mean it doesn't work like that everywhere?

No, you can for example have real fun in Ireland... The addresses
sometimes start with 1 .. n on one side of the street and continue
from the "n" end with n+k .. max back to the house opposite no 1.
Even better though _very_ seldom, here in Germany: Some old villages
where the houses are numbered in the order they have been built in :)
Dunno what is happening on the left side on the pond...


Cheers
Michael
 
F

Flash Gordon

Joona said:
Chris Croughton said:
Consider the addresses 1562 Ram Street and 1838 Ram Street.

[...]

But if we subtract one from the other, we get 276 - the number
of houses you'd have to walk past to get from one to the other
(including one of the end points). This turns out to be a
useful number.

You must live in a place with a different approach to addresses.
By the e-address, infobahn is in Britain, where adresses di often work
like that (although more of them are word addresses, incrementing by two
with odd bytes -- er, houses -- on one side and even ones on the other).

You mean it doesn't work like that everywhere?

It isn't even like that everywhere in the UK. The house to one side of
me is one less, to the other side it is the same as me only with "a"
appended. Some roads are even stranger.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,780
Messages
2,569,609
Members
45,253
Latest member
BlytheFant

Latest Threads

Top