Comparing pointers

C

Chris Dollin

George said:
p or q may be interior pointers to a malloc'd block.

Just comparing them for equality with the base address won't work.
You need to compare base <= p < (base+size), at which point you're
back to undefined behavior if p is not a pointer into the block.

I think J was thinking of the brute-force approach:

To see if `p` is in the block, compute all pointers from `base` to
`base + size` and compare `p` with each of them /individually/.
But it still won't help in the general case. Technically, by the
standard, comparing unrelated pointers in any way (even for equality)
produces undefined behavior.

Are you sure? (Spikey-speak for "cites, please".)
The exception is that any pointer may be
compared against the constant NULL ... but then the value of NULL is
implementation defined and _not_ guaranteed to be zero.

It's guaranteed to be a way of writing a null pointer constant,
though, as is 0 in a pointer context.
 
G

George Neuner

Are you sure? (Spikey-speak for "cites, please".)

The following is long because the relevant information is spread over
several sections. It is taken from the annotated version of the C99
standard, however IIRC, the only difference vs C89/C90 WRT pointers is
the inclusion of array[nelems+1] as part of the array object's address
space.

I'm pretty sure the section/paragraph #'s are the same in the
unannotated version but I can't check it just now.

Stuff not applicable to pointer comparison has been elided.


--
6.3.2.3 Conversions (Pointers)

739 A pointer to void may be converted to or from a pointer to any
incomplete or object type.

740 A pointer to any incomplete or object type may be converted to a
pointer to void and back again;

741 the result shall compare equal to the original pointer.

742 For any qualifier q, a pointer to a non-q-qualified type may be
converted to a pointer to the q-qualified version of the type;

743 the values stored in the original and converted pointers shall
compare equal.

744 An integer constant expression with the value 0, or such an
expression cast to type void *, is called a null pointer constant.

Commentary: Many implementations use an execution-time representation
of all bits zero as the value of the null pointer constant.
Implementations for processors that use a segmented memory
architecture have a number of choices for the representation of the
null pointer. The Inmos Transputer has a signed address space with
zero in the middle. It used the value 0x80000000 as its execution-time
representation. The IBM/390 running CICS also used this value.

Technically there is a distinction between a null pointer constant and
a null pointer. In practice implementations rarely make a distinction.
It is debatable whether there is a worthwhile benefit in trying to
educate developers to distinguish between the two terms.

744.1 The null pointer constant shall only be represented in the
visible form of source code by the NULL macro.

745 If a null pointer constant is converted to a pointer type, the
resulting pointer, called a null pointer, is guaranteed to compare
unequal to a pointer to any object or function.

746 Conversion of a null pointer to another pointer type yields a null
pointer of that type.

747 Any two null pointers shall compare equal.
--

6.5.8 Relational Operators

1193 For the purposes of these operators, a pointer to an object that
is not an element of an array behaves the same as a pointer to the
first element of an array of length one with the type of the object as
its element type.

1194 When two pointers are compared, the result depends on the
relative locations in the address space of the objects pointed to.

Commentary: Here the term "address space" refers to the object within
which the two pointers point. It is not being used in the common usage
sense of the address space of the program, which refers to all the
storage locations available to an executing program. The standard does
not define the absolute location of any object or subobject. However,
in some cases it does define their locations relative to other
subobjects.

1195 If two pointers to object or incomplete types both point to the
same object, or both point one past the last element of the same array
object, they compare equal.

1196 If the objects pointed to are members of the same aggregate
object, pointers to structure members declared later compare greater
than pointers to members declared earlier in the structure, and
pointers to array elements with larger subscript values compare
greater than pointers to elements of the same array with lower
subscript values.

1197 All pointers to members of the same union object compare equal.

1198 If the expression P points to an element of an array object and
the expression Q points to the last element of the same array object,
the pointer expression Q+1 compares greater than P.

1199 In all other cases, the behavior is undefined.

Commentary: The C relational operator model enables pointers to
objects to be treated in the same way as indexes into array objects.
Relational comparisons between indexes into two different array
objects (that are not both subobjects of a larger object) rarely have
any meaning and the standard does not define such support for
pointers. Some applications do need to make use of information on the
relative locations of different objects in storage. However, this
usage was not considered to be of sufficient general utility for the
Committee to specify a model defining the behavior.
--

6.5.9 Equality Operators

1203 One of the following shall hold:

:

1205 both operands are pointers to qualified or unqualified versions
of compatible types;

Commentary: The rationale for supporting pointers to qualified or
unqualified type is the same as for pointer subtraction and relational
operators. Differences in the qualification of pointed-to types is
guaranteed not to affect the equality status of two pointer values.
The operands may have a pointer to function type.

The discussion on the relational operators is applicable here.

1206 one operand is a pointer to an object or incomplete type and the
other is a pointer to a qualified or unqualified version of void;

Commentary:This combination of operands supports the idea that pointer
to void represents a generic container for any pointer type. Relaxing
the constraint in the previous C sentence, requiring the pointer types
to be compatible, removes the need for an explicit cast of the operand
having pointer to void type. A pointer to void is only guaranteed to
compare equal to the original pointer value when it is converted back
to that value’s original pointer type.

1207 one operand is a pointer and the other is a null pointer
constant.

:

1220 If one operand is a pointer and the other is a null pointer
constant, the null pointer constant is converted to the type of the
pointer.

1221 If one operand is a pointer to an object or incomplete type and
the other is a pointer to a qualified or unqualified version of void,
the former is converted to the type of the latter.

1222 For the purposes of these operators, a pointer to an object that
is not an element of an array behaves the same as a pointer to the
first element of an array of length one with the type of the object as
its element type.

1223 Two pointers compare equal if and only if both are null pointers,
both are pointers to the same object (including a pointer to an object
and a subobject at its beginning) or function, both are pointers to
one past the last element of the same array object, or one is a
pointer to one past the end of one array object and the other is a
pointer to the start of a different array object that happens to
immediately follow the first array object in the address space.
--



AFAICT comparisons between pointers, including equality, are defined
only for

1) "compatible" pointers, which by 1194 are defined as pointers of the
same type pointing into the same address space - ie. to the same
object or into the same array.
2) Pointers to void including expressions involving one pointer to
void and one pointer of another type. The non-void pointer is cast to
void before the comparison is made.
3) Null pointer constants including expressions involving one null
pointer constant and one pointer of another type. The null constant
pointer is cast to the other pointer's type before the comparison is
made.

By my reading, pointers to unrelated objects, excepting to different
members of the same array, do not meet the "address space" requirement
of the relational operators (1194) and thus are not "compatible" for
the purposes of equality (1205).

The fact that it just works in most implementations does not make it
either correct or portable.

It's guaranteed to be a way of writing a null pointer constant,
though, as is 0 in a pointer context.

Almost. ((void*)0) is guaranteed to be a null pointer constant and
that pointer constant may be further cast to another pointer type.
Directly casting zero to a non-void pointer type does not, AFAICT,
portably yield a null pointer constant.

George
 
K

Keith Thompson

George Neuner said:
Are you sure? (Spikey-speak for "cites, please".)

The following is long because the relevant information is spread over
several sections. It is taken from the annotated version of the C99
standard, however IIRC, the only difference vs C89/C90 WRT pointers is
the inclusion of array[nelems+1] as part of the array object's address
space.

I'm pretty sure the section/paragraph #'s are the same in the
unannotated version but I can't check it just now.

[big snip]
By my reading, pointers to unrelated objects, excepting to different
members of the same array, do not meet the "address space" requirement
of the relational operators (1194) and thus are not "compatible" for
the purposes of equality (1205).

The fact that it just works in most implementations does not make it
either correct or portable.

I think you've misinterpreted. You really only need two paragraphs
from the standard, C99 6.5.9p5-6 (Equality operators):

Otherwise, at least one operand is a pointer. If one operand is a
pointer and the other is a null pointer constant, the null pointer
constant is converted to the type of the pointer. If one operand
is a pointer to an object or incomplete type and the other is a
pointer to a qualified or unqualified version of void, the former
is converted to the type of the latter.

Two pointers compare equal if and only if both are null pointers,
both are pointers to the same object (including a pointer to an
object and a subobject at its beginning) or function, both are
pointers to one past the last element of the same array object, or
one is a pointer to one past the end of one array object and the
other is a pointer to the start of a different array object that
happens to immediately follow the first array object in the
address space

The preceding section, on relational operators (<, <=, >, >=)
specifically states that the behavior is undefined except in certain
cases. There is no such statement in the section on equality
operators. As long as both pointers have valid (not indeterminate)
values, "=" and "!=" will yield 1 and 0, respectively, if they're
equal, and 0 and 1, respectively, if they aren't.
Almost. ((void*)0) is guaranteed to be a null pointer constant and
that pointer constant may be further cast to another pointer type.
Directly casting zero to a non-void pointer type does not, AFAICT,
portably yield a null pointer constant.

Whether something is a null pointer constant is not a matter of
portability. A null pointer constant is an unambiguous source code
construct, not to be confused with a null pointer, which is a run-time
value. Specifically, C99 6.3.2.3p3:

An integer constant expression with the value 0, or such an
expression cast to type void *, is called a _null pointer
constant_. If a null pointer constant is converted to a pointer
type, the resulting pointer, called a _null pointer_, is
guaranteed to compare unequal to a pointer to any object or
function.

So casting anything to a type other than void* cannot yield a null
pointer constant, but converting a null pointer constant to any
pointer type yields (at run time) a null pointer value.

(There's a slight glitch in the wording of the standard, such that
it's not clear that a parenthesized null pointer constant such as
((void*)0) is a null pointer constant, but it's clearly intended that
it should be.)
 
W

Walter Roberson

Keith Thompson said:
I think you've misinterpreted. You really only need two paragraphs
from the standard, C99 6.5.9p5-6 (Equality operators):
Two pointers compare equal if and only if both are null pointers,
both are pointers to the same object (including a pointer to an
object and a subobject at its beginning) or function, both are
pointers to one past the last element of the same array object, or
one is a pointer to one past the end of one array object and the
other is a pointer to the start of a different array object that
happens to immediately follow the first array object in the
address space

FYI, C89 3.5.9 lacked the 'if and only if', and used different
phrasing that was a little ambiguous:

If two pointers to object or incomplete types compare equal,
they are both null pointers, or both point to the same object, or
both point one past the last element of the same array object.

This wording doesn't -quite- indicate that comparing between different
objects is defined: "if this succeeds then", but if it doesn't succeed
then possibly the comparison results in 0 or possibly you get
some kind of exception. There is nothing in the Constraints portion
of the C89 section to indicate that comparing between different objects
is disallowed, which makes the "or possibly you get some kind of
exception" argument noticably weaker (some might say denies it entirely.)
 

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

Similar Threads


Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top