指针减法和替代方案
Pointer Subtraction and an Alternative
当使用数组时,标准算法(在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 亿 GB 的数组。在 32 位实现中,通常您的总地址空间限制为 3 GB,如果幸运的话,为 3 1/4 GB(重要的是地址空间,而不是 RAM),因此您需要一个超过 2 GB 的数组,这不会留下太多其他东西。而且 malloc 很可能首先会拒绝分配这种大小的数组。当然是你的判断。
虽然 std::d istance 有优势,但我怀疑它与 ptrdiff_t 具有相同的理论问题,因为距离可以是正数和负数,而且它可能不是 32 位实现上的 64 位类型。
请注意,如果您可以在 32 位实现上分配一个 3 GB 数组,并且该数组的第一个和最后一个元素有两个 int*,那么即使结果适合ptrdiff_t,如果指针差计算不正确,我也不会感到惊讶。
可能的解决方法:
将两个指针投射到uintptr_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 起)
- 1d 智能指针不适用于语法 (*)++
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 运行同一解决方案的另一个项目的项目
- 为什么使用 "this" 指针调用派生成员函数?
- 函数向量_指针有不同的原型,我可以构建一个吗
- 使用指针从C++中的数组中获取最大值
- 助记符和指向成员语法的指针
- 嵌入方指针压缩已禁用
- Project Euler问题4的错误解决方案
- 数组的指针从不分段故障
- C++ 指针的内存地址和指向数组的内存地址如何相同?
- 何时在引用或唯一指针上使用移动语义
- NULL指针解决方案中的C 分割故障
- 向一系列指针及其替代方案的动态分配
- 正在(在构造函数中)将其包含一个不良设计的指针传递,如果是的,则解决方案是什么
- 指针减法和替代方案
- 函数指针的通用映射.有可能吗?任何替代方案
- std::vector 的替代方案,因为重新分配会使指向元素的指针无效
- 函数指针:从性能的角度来看,简单的规范使用是否不好?如果是这样的话,c++11的替代方案是什么
- C++中返回局部变量的引用和标准指针的替代方案是什么