我正在使用N3797工作草案。
第5.7 / 1节说:
[...]另外,两个操作数都应该是算术或 unscoped枚举类型,或一个操作数应该是指向a的指针 完全定义的对象类型,另一个应具有整数或 无范围的枚举类型。
确定。但请考虑3.7.4.3/3节中的规则:
- 加法或按位运算的结果,其中之一 operands是安全派生指针的整数表示 值
P
,如果由reinterpret_cast<void*>
转换的结果 比较等于可计算的安全派生指针reinterpret_cast<void*>(P)
。
也就是说,我们不能将指针算法应用于指向void
的指针。您是否可以提供反映 3.7.4.3 ?
类似的question没有提供合适的示例,因为指向void算术的指针在这里出现。
答案 0 :(得分:2)
3.7.4.3/3所说的就是如果你有一个安全派生指针&#34;的&#34;整数表示,并用它做数学,结果只是一个有效的&#34;整数表示安全派生的指针&#34;如果你可以用指针算法和单独投射得到相同的结果。
虽然不允许对void*
directy进行算术运算,但还有其他各种方法可以从void*
获取有效指针。虽然我懒得用标准中的引号支持每一步,但以下示例应该是有效的:
double arr[10];
double* P = &arr[0]; // safely derived pointer
intptr_t N = reinterpret_cast<intptr_t>(P); // valid integer repr
void* V = reinterpret_cast<void*>(P);
// Compute &a[1] from V
void* V2 = reinterpret_cast<void*>(
static_cast<char*>(V) + sizeof(double));
// Do the same with N
intptr_t N2 = N + sizeof(double);
assert(reinterpret_cast<void*>(N2) == V2);
reinterpret_cast<void*>(P)
由于V2和N2比较相等,N2是安全导出指针的&#34;整数表示&#34;同样。
答案 1 :(得分:0)
垃圾收集实现需要跟踪指针。实际上,这通常是通过使用内存映射作为键来完成的,因此在某个数值范围内的内存中的任何指针大小的单词都被视为真正的指针,并且因此防止包括给定地址的分配块的垃圾收集-值。这种策略是不精确的,C ++旨在做得更好,同时也支持现有的GC程序。
与你的直觉相反,子弹 有效地启用void*
上的指针算法。&#34;从std::intptr_t
指针获得的整数(reinterpret_cast
)也必须作为指针被跟踪,并且该对象继续影响垃圾收集,即使它是通过加法或按位运算修改的。
std::uintptr_t handle1 = reinterpret_cast< std::uintptr_t >( new foo );
std::uintptr_t handle2 = reinterpret_cast< std::uintptr_t >( new foo );
// handle1 and handle2 won't be collected, despite no pointer-type references.
std::uintptr_t diff = handle2 - handle1; // not a pointer
std::uintptr_t sum = handle1 + diff; // sum refers to handle2
handle2 = 0; // Invalidate original reference to handle2
// At this point, the second foo is still reachable via sum.
严格的指针安全性改善了现状,但是当满足规则的操作不生成实际指针表示时,仍然可能出现误报。例如,从标准文本中不清楚diff
是否可追溯,因为它是两个安全派生操作数的差异运算的结果,而不是仅添加一个安全派生的操作数。 (无论是否有些无关紧要,无论如何都会发生误报。)