5

Inspired by this answering this question, I dug a little into the C11 and C99 standards for the use of equality operators on pointers (the original question concerns relational operators). Here's what C11 has to say (C99 is similar) at §6.5.9.6:

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.94)

Footnote 94 says (and note that footnotes are non-normative):

Two objects may be adjacent in memory because they are adjacent elements of a larger array or adjacent members of a structure with no padding between them, or because the implementation chose to place them so, even though they are unrelated. If prior invalid pointer operations (such as accesses outside array bounds) produced undefined behavior, subsequent comparisons also produce undefined behavior.

The body of the text and the non-normative note appear to be in conflict. If one takes the 'if and only if' from the body of the text seriously, then in no other circumstances than those set out should equality be returned, and there is no room for UB. So, for instance this code:

uintptr_t a = 1;
uintptr_t b = 1;
void *ap = (void *)a;
void *bp = (void *)b;
printf ("%d\n", ap <= bp); /* UB by §6.5.8.5 */
printf ("%d\n", ap < bp);  /* UB by §6.5.8.5 */
printf ("%d\n", ap == bp); /* false by §6.5.9.6 ?? */

should print zero, as ap and bp are neither pointers to the same object or function, or any of the other bits set out.

In §6.5.8.5 (relational operators) the behaviour is more clear (my emphasis):

When two pointers are compared, the result depends on the relative locations in the address space of the objects pointed to. 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. 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. All pointers to members of the same union object compare equal. 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. In all other cases, the behavior is undefined.

Questions:

  • I am correct that there is some ambiguity as to when equality operators with pointers are permitted UB (comparing the footnote and the body of the text)?

  • If there is no ambiguity, when precisely can comparison of pointers with equality operators be UB? For instance, is it always UB if at least one pointer is artificially created (per above)? What if one pointer refers to memory that has been free()d? Given the footnote is non-normative, can one conclude there is never UB, in the sense that all 'other' comparisons must yield false?

  • Does §6.5.9.6 really mean that equality comparison of meaningless but bitwise equal pointers should always be false?

Note this question is tagged ; I am not asking what in practice compilers do, as I believe already know the answer to that (compare them using the same technique as comparing integers).

Community
  • 1
  • 1
abligh
  • 24,573
  • 4
  • 47
  • 84
  • Perhaps one manifestation of undefined behavior is to have two pointers compare as equal even though the equality is semantically meaningless. Undefined behavior can interact with defined behavior in complicated ways, without thereby becoming defined. – John Coleman Nov 26 '16 at 18:26
  • @JohnColeman I agree, but §6.5.9.6 does not permit UB at all by my reading. It insists that equality comparisons of bit-wise equal but meaningless pointers should be false. I've edited the question so that this point is clearer. Thanks. – abligh Nov 26 '16 at 18:29
  • 1
    You are probably correct that there is an ambiguity. The first thing that you quoted didn't say anything about *valid* pointers comparison, although that was likely the intent. Good question. – John Coleman Nov 26 '16 at 18:35
  • 2
    Remember that footnotes in ISO standards are not normative — they provide information, but do not define the language. In case of doubt, the non-footnote material rules. That's pure information to you; not in any sense an answer to your question. – Jonathan Leffler Nov 26 '16 at 18:40
  • @JonathanLeffler thanks - indeed they are (I've clarified the question to show I do not mean mean otherwise); that strengthens the case for the somewhat bizarre conclusion I drew with the code example. – abligh Nov 26 '16 at 18:43
  • Evaluating `(void*)a` might have undefined behavior. I'll have to check the standard to be sure. – Keith Thompson Nov 26 '16 at 18:46
  • §6.5.9.6 doesn't seem to say anything about when pointers must compare unequal, only when they must compare equal. I don't see how it says that all possible pointer comparisons other than the ones listed there must evaluate to false. – Crowman Nov 26 '16 at 18:47
  • @KeithThompson not per §6.5.4 and the long non-exhaustive list of UB at § implies not: *'A pointer is converted to **other than an integer** or pointer type (6.5.4).'* (I realise that's in the other direction). Also POSIX ( http://pubs.opengroup.org/onlinepubs/000095399/basedefs/stdint.h.html ) says *"The following type designates a signed integer type with the property that any valid pointer to void can be converted to this type, then converted back to a pointer to void, and the result will compare equal to the original pointer: `intptr_t`"* and it would be odd if that exploited UB. – abligh Nov 26 '16 at 18:54
  • @PaulGriffiths: see §6.5.9.3 – abligh Nov 26 '16 at 18:55
  • @abligh: I don't think that applies if it's UB to make the comparison in the first place. – Crowman Nov 26 '16 at 18:57
  • If a program has undefined behavior, then its behavior is undefined. It may behave during execution in a way that appears to contradict the requirements of the standard, because undefined behavior is behavior for which the standard imposes no requirements. – Oktalist Nov 26 '16 at 19:01
  • 1
    @PaulGriffiths but there is nothing normative to say it is UB! It says *'two pointer compare equal if and only if [conditions]'* (which means they are either equal or not equal), then §6.5.9.3 describes how equality is reflected in the behaviour of the operators. The very things that make §6.5.8.5 UB fall outside *'[conditions]'* in the same way that two non-equal valid pointers fall outside *'[conditions]'*, so unless comparing any two non-equal pointers is UB (can't be true!), all behaviour appears defined. – abligh Nov 26 '16 at 19:02
  • 1
    @Oktalist and which part of the standard makes the comparison UB? I'm arguing the behaviour *is* defined. – abligh Nov 26 '16 at 19:03
  • @abligh: §6.5.9.3 also says: "The `==` (equal to) and `!=` (not equal to) operators are analogous to the relational operators except for their lower precedence", which I read to mean that "When two pointers are compared...In all other cases, the behavior is undefined" applies to `==` and `!=`, too. For "two pointers compare equal if and only if..." to apply at all, I think you first have to be in one of the allowed comparisons from §6.5.8.5. If you're not, I think that's where the UB comes from. – Crowman Nov 26 '16 at 19:07
  • 1
    @PaulGriffiths that can't be right. You are surely allowed to compare two pointers which are (e.g.) returns from `malloc()` or `mmap()` with `==`, even if comparing them with `<` is UB. – abligh Nov 26 '16 at 19:09
  • As far as equality of pointers to different objects, the pointer to a structure and a pointer to the first member of a structure have equal addresses. For example, with most compilers, &structure == &structure.first_member. – rcgldr Nov 26 '16 at 19:12
  • 1
    @abligh: Why "surely"? – Crowman Nov 26 '16 at 19:12
  • @PaulGriffiths it would make (for instance) traversing a linked list hard! (either a `NULL` terminated one or one that is a circle). In fact comparing a pointer with `p == NULL` would be UB by this interpretation (as `p > NULL` is UB). – abligh Nov 26 '16 at 19:24
  • @abligh If you're arguing the behaviour _is_ defined (which wasn't clear from the question) then you should look for a quote in the standard to support that claim (i.e. that `ap == bp` in your example is defined to be false). – Oktalist Nov 26 '16 at 19:28
  • @abligh: `NULL` is a special case, and guaranteed to compare unequal to any pointer per §6.3.2.3.3. Traversing a circular list by comparing the pointers would appear troublesome, for sure, if the elements weren't members of an array. – Crowman Nov 26 '16 at 19:34
  • @abligh An implementation could support POSIX `intptr_t` simply by defining and documenting the relevant behaviour which the C standard left undefined. §3.4.3(2): _"Possible undefined behavior ranges from [...] to behaving in a documented manner characteristic of the environment."_ In C99 `intptr_t` became part of the C standard. – Oktalist Nov 26 '16 at 20:14
  • §6.5.9 ¶7 says _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._. Does that mean, I wonder, that it is valid to form the address `&a+1` as 'one address past the end of the array of length one'? It's probably tangential to this question; I'm still cogitating about what the question is asking and what the standard says. – Jonathan Leffler Nov 26 '16 at 22:29
  • @JonathanLeffler: That point came up in relation to another question, recently, although I don't have a link to hand. I believe the consensus was that yes, that's exactly what it means. For instance, if you pass a pointer to an array on `int` of length one to a function, I don't think there's any way for a function to distinguish this from a pointer to a single `int`, so it seems like you'd have to allow `a+1` for both possibilities, or for neither. – Crowman Nov 26 '16 at 23:08
  • @John Coleman: The Standard describes various common consequences of UB, and notes in the Rationale that selection among them is a *Quality of Implementation* issue, and it also notes that the Standard makes no attempt to require that conforming compilers be of sufficient quality to be suitable for any particular purpose. I'm not sure why compiler writers have become so interested in what behaviors are allowed in low-quality-but-conforming compilers rather than in what behaviors would make their compilers most useful, but for whatever reason such vandals seem to be tolerated. – supercat Jul 11 '18 at 16:35

1 Answers1

-1

Am I correct that there is some ambiguity as to when equality operators with pointers are UB?

No, because this passage from §6.5.9(3):

The == and != operators are analogous to the relational operators except for their lower precedence.

Implies that the following from §6.5.9(6) also applies to the equality operators:

When two pointers are compared [...] In all other cases, the behavior is undefined.

If there is no ambiguity, when precisely can comparison of pointers with equality operators be UB?

There is undefined behaviour in all cases for which the standard does not explicitly define the behaviour.

Is it always UB if at least one pointer is artificially created converted from an arbitrary integer?

§6.3.2.3(5):

An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.

What if one pointer refers to memory that has been freed?

§6.2.4(2):

The value of a pointer becomes indeterminate when the object it points to reaches the end of its lifetime.

can one conclude there is never UB, in the sense that all 'other' comparisons must yield false?

No. The standard defines under what conditions two pointers must compare equal, and under what conditions two pointers must compare not equal. Any equality comparisons between two pointers that falls outside both of those two sets of conditions invokes undefined behaviour.

Does §6.5.9(6) really mean that equality comparison of meaningless but bitwise equal pointers should always be false?

No, it is undefined.

Oktalist
  • 14,336
  • 3
  • 43
  • 63
  • 2
    I'm not the downvoter, but your answer fails to address the matter of 6.5.9/6. One of the critical points in the question is whether 6.5.9/6 should expand the applicability of equality comparisons beyond what's allowed for relational comparisons. You seem to claim that 6.5.9/3 trumps 6.5.9/6. That's not convincing. – AnT stands with Russia Jul 10 '18 at 23:46
  • 3
    Didn't downvote, but `==` and `!=` falling under comparison operators for the purposes of §6.5.9(6) is most unconvincing. §6.5.8(5) (When two pointers are compared ...) is under Relational operators (does not include == and !=), analogous to me does not imply §6.5.8(5) should apply, and if equality-comparison of unrelated objects were UB, then things like comparing fn-pointers would be UB, which seems questionable. – Petr Skocik Jul 11 '18 at 00:11
  • The authors of the Standard recognize the possibility of implementations that are conforming, but are of such low quality as to be essentially useless. I personally wouldn't read the Standard as giving blanket permission for conforming-but-useless implementations to have an equality comparison between unrelated pointers do something other than yield 0 or 1, but can accept that honorable people might read it differently. On the other hand, I don't see why someone who isn't trying to undermine the language should try to use that an excuse for "optimization". – supercat Jul 11 '18 at 19:31