PoweredByRice
PoweredByRice

Reputation: 2509

2 pointers, 0 bytes difference but not equal

In C++ I am fully aware pointers subtraction is only valid within an array, and the code below is undefined behaviour. I am aware trying to reason about undefined behaviour is next to pointless, however I believe there is value in asking the following question.

#include <cstddef>
#include <iostream>
#include <iomanip>

int main()
{
    long a = 1;
    long b = 1;

    std::cout << (char*)(&b) - (char*)(&a)  << '\n'; //prints 8, ok we're 8 bytes apart

    char* aPlus8 = (char*)&a + 8; //bump up 8 bytes
    char* bPtr = (char*)&b;

    std::cout << "diff in bytes = " << (bPtr - aPlus8)  << '\n';            //prints 0. All good, looks like we're there
    std::cout << "but are they the same? = " << (bPtr == aPlus8)  << '\n';  //but are we ?
}

The last line bPtr == aPlus8 returns false, although the difference in bytes is 0. Is there possibly an explanation for this? (other than "because it's undefined behaviour")

This is compiled with g++ -std=c++14 -O3 -Wall -pedantic. If I change the optimisation level then the outputs also change, expectedly.

Upvotes: 0

Views: 73

Answers (2)

supercat
supercat

Reputation: 81217

The C Standard, and likely the C++ Standard, makes clear that if two pointers may "legitimately" access the same object they will compare equal, and also makes clear that a pointer to one object may sometimes compare equal to a pointer "just past" another object even if they cannot legitimately access the same object. In most cases properly-designed code shouldn't care whether a pointer to an object would compare equal to a pointer that is "just past" another, and so I don't think either standard makes clear whether:

  1. If two objects are unrelated, each individual comparison between a just-past pointer for one and a pointer to another should be regarded as yielding an independent Unspecified result (meaning the pointer-equality operator need not behave as an equivalence relation).

  2. The set of unrelated objects which may be connected by "direct" and "just-past" pointers is Unspecified, but comparisons involving such pointers will yield results consistent with some such set (meaning the pointer-equality operator would effectively define an equivalence relation).

  3. The semantics of pointer equality are defined in some fashion more precise than #1, but less precise than #2.

The Standard deliberately refrains from requiring that all implementations be suitable for systems programming, and compilers may generate more efficient code if they're allowed to make all comparisons involving pointers to unrelated objects report that they are not equal. It would be helpful to have a standard means by which code for purposes like systems programming could specify that it won't work without certain semantic guarantees beyond those normally required by the Standard, and that any implementation that can't honor such guarantees must refuse compilation altogether. I don't know of any way in C or C++ to specify that code needs the pointer-equality operator to behaves as an equivalence relation, but C++ has other means of comparing pointers, such as std::less, which offer stronger guarantees than its operators.

Upvotes: 0

Most likely the optimizer noticed that bPtr and aPlus8 could not possibly be equal, so it replaced (bPtr == aPlus8) with false to save some CPU instructions.

Note that this kind of optimization doesn't just save CPU instructions - imagine if you had something like

if(bPtr == aPlus8)
{
    // lots of complicated code here
}

then the optimizer would be able to remove all the code inside the if statement. That's why this optimization and similar ones are useful.

In practice, on a modern compiler, one of the main impacts of undefined behaviour is that it allows the optimizer to find ways to simplify your code that you didn't expect.

Upvotes: 6

Related Questions