指针减法和替代

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

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

这总是看起来简单而有效。 但是,最近我指出指针减法实际上返回了一个ptrdiff_t ,原则上,如果“ index ”不适合ptrdiff_t ,可能会出现问题。 我真的不相信任何实施都会有足够的反对意见,允许一个人创建如此大的arr(从而引起这样的问题),但是这里接受的答案承认这是可能的,而且我没有发现任何其他证据。 因此,我已经辞职了(除非有人能说服我),并且会小心前进。 那个答案提出了一个相当复杂的“安全”获取索引的方法; 真的没有更好的吗?

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

你不可能有两个指向相同数组的指针,其差异不适合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。

可能的解决方法:

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

真正便携(但效率较低)的解决方法:

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

我没有找到任何证据表明不这样做。

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

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