指针减法和替代

时间:2015-05-08 12:00:58

标签: c++ c pointers

使用数组时,标准算法(在C和C ++中)通常会返回指向元素的指针。有时可以方便地获得元素的索引,也许是为了索引到另一个数组,我通常通过从指针中减去数组的开头来得到它:

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

这似乎总是很简单有效。然而,最近我指出,指针减法实际上返回ptrdiff_t,原则上,如果" index"不适合ptrdiff_t。我并不是真的相信任何实现都会有足够的反复意义让人们创建这么大的arr(从而引起这样的问题),但接受的答案here承认这是可能的,而且我没有发现任何其他证据。因此,我已经因此而辞职(除非有人能说服我),并且会小心前进。这个答案提出了一个相当复杂的方法,安全地#34;得到索引;真的没有更好的吗?

那就是说,我对C ++中可能的解决方法感到困惑。我们有std::distance,但std::distance(arr, addressICareAbout)保证定义明确吗?一方面,(指向第一个元素的指针)arr可以递增到addressICareAbout(对吗?),但另一方面std::distance应该返回{{1} }}。标准容器的迭代器可能(可能)具有相同的问题。

3 个答案:

答案 0 :(得分:5)

你不可能有两个指向相同数组的指针,其差异不适合ptrdiff_t。

在64位实现上,ptrdiff_t被标记为64位,因此您需要一个80亿千兆字节的数组。在32位实现中,通常你的总地址空间限制为3 GB,如果幸运的话,它是3 1/4 GB(它的地址空间,而不是RAM,这很重要),所以你需要一个超过2 GB的数组,不会留下太多其他东西。并且很可能malloc将首先拒绝分配该大小的数组。你的判断当然。

虽然std :: distance具有优势,但我怀疑它与ptrdiff_t具有相同的理论问题,因为距离可以是正的和负的,并且在32位实现上它可能不是64位类型。

注意,如果你可以在32位实现上分配一个3 GB的数组,并且你对该数组的第一个和最后一个元素有两个int *,如果指针差异计算不正确,我也不会感到惊讶虽然结果符合ptrdiff_t。

答案 1 :(得分:0)

可能的解决方法:

将两个指针投射到uintptr_t,减去并自己除以sizeof (T)。这不是精确的可移植性,但它保证永远不会是未定义的行为,并且大多数系统以使其工作的方式指定整数< - >指针转换。

真正可移植(但效率较低)的解决方法:

使用替代基指针。如果数组超过2<<30个元素,那么您可以合法地计算step = p1 + (2<<30),使用关系运算符来查看是否p2 > step,如果是,则将偏移计算为(2u << 30) + uintptr_t(distance(step, p2))注意递归调用可能需要采取另一个步骤。

答案 2 :(得分:-2)

  

我发现没有其他证据可以暗示。

幸运的是,这里有证据:http://en.cppreference.com/w/cpp/types/size_t

  

std::size_t可以存储任何类型(包括数组)理论上可能的对象的最大大小。其大小不能由std::size_t表示的类型格式不正确(因为C ++ 14)