Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pointer Subtraction and an Alternative

Tags:

c++

c

pointers

When working with arrays, standard algorithms (in both C and C++) often return pointers to elements. It is sometimes convenient to have the index of the element, perhaps to index into another array, and I normally get that by subtracting the beginning of the array from the pointer:

int arr[100];
int *addressICareAbout = f(arr, 100);
size_t index = addressICareAbout - arr;

This always seemed simple and effective enough. However, it was recently pointed out to me that pointer subtraction actually returns a ptrdiff_t and that, in principle, there could be problems if the "index" doesn't fit in a ptrdiff_t. I didn't really believe that any implementation would be perverse enough to allow one to create such a large arr (and thereby cause such issues), but the accepted answer here admits that that's possible and I've found no evidence to suggest otherwise. I've therefore resigned myself to this being the case (unless someone can convince me otherwise) and will be careful going forward. That answer proposes a fairly convoluted method of "safely" getting the index; is there really nothing better?

That said, I'm confused about a possible workaround in C++. There we have std::distance, but is std::distance(arr, addressICareAbout) guaranteed to be well-defined? On the one hand, (the pointer to the first element of) arr can be incremented to reach addressICareAbout (right?), but on the other hand std::distance should return a ptrdiff_t. The iterators for the standard containers can (presumably) have the same issues.

like image 267
Joshua Green Avatar asked May 08 '15 12:05

Joshua Green


1 Answers

It is extremely unlikely that you will ever have two pointers to the same array where the difference doesn't fit into ptrdiff_t.

On 64 bit implementations, ptrdiff_t is signed 64 bit, so you'd need an array of 8 billion gigabytes. On 32 bit implementations, usually your total address space is limited to 3 GB, 3 1/4 GB if you are lucky (it's address space, not RAM, that counts), so you'd need an array of more than 2 GB which doesn't leave much for anything else. And it is quite possible that malloc will refuse to allocate an array of that size in the first place. Your judgement of course.

While std::distance has advantages, I suspect it has the same theoretical problem as ptrdiff_t, since distances can be positive and negative, and it's probably not a 64 bit type on a 32 bit implementation.

Note that if you can allocate a 3 GB array on a 32 bit implementation, and you have two int* to the first and the last element of that array, I wouldn't be surprised if the pointer difference is calculated incorrectly even though the result fits into ptrdiff_t.

like image 54
gnasher729 Avatar answered Oct 12 '22 13:10

gnasher729