在删除的数组上进行指针运算仍然合法吗?

时间:2013-12-13 15:16:38

标签: c++ c++11 language-lawyer undefined-behavior pointer-arithmetic

今天我写了一些看起来像这样的东西:

void foo(std::vector<char>&v){
    v.push_back('a');
    char*front=&v.front();
    char*back=&v.back();
    size_t n1=back-front+1;
    v.push_back('b');//This could reallocate the vector elements
    size_t n2=back-front+1;//Is this line valid or Undefined Behavior ?
}

如果我再次按'b'时会发生重新分配,我还可以计算两个指针的差异吗?

在阅读了相关标准的几次之后,我仍然无法在这一点上思考。

C ++ 11 5.7.6 : 当减去指向同一数组对象元素的两个指针时,结果就是它的差异 两个数组元素的下标。结果的类型是实现定义的有符号整数 类型;此类型应与标题(18.2)中定义为std :: ptrdiff_t的类型相同。如 对于任何其他算术溢出,如果结果不适合所提供的空间,则行为未定义。 换句话说,如果表达式P和Q分别指向数组对象的第i个和第j个元素, 表达式(P) - (Q)具有值i-j,条件是该值适合std :: ptrdiff_t类型的对象。 此外,如果表达式P指向数组对象的元素或者指向最后一个元素的元素 一个数组对象,表达式Q指向同一个数组对象的最后一个元素,即表达式 ((Q)+1) - (P)具有与((Q) - (P))+ 1和 - ((P) - ((Q)+1))相同的值,并且如果值为零则为零该 表达式P指向数组对象的最后一个元素之后的一个,即使表达式(Q)+1没有 指向数组对象的元素。 除非两个指针都指向同一个数组对象的元素,否则 在数组对象的最后一个元素之后,行为未定义。

当然我知道它有效,我只是想知道它是否合法。

3 个答案:

答案 0 :(得分:7)

删除对象的指针是有毒的:除了给它们一个新值之外,不要触摸任何其他东西。存储器跟踪系统可以捕获对回收的指针值的使用。但是,我不知道是否存在这样的系统。

相关引用是3.7.4.2 [basic.stc.dynamic.deallocation]第4段:

  

如果在标准库中赋予释放函数的参数是一个不是空指针值的指针,则释放函数将释放指针引用的存储,使得所有指向的任何部分的指针无效解除分配存储。使用无效指针值(包括将其传递给释放函数)的效果未定义。

当调整std::vector<...>的大小时,它会跳过许多箍(分配器),默认情况下最终会调用释放函数。

答案 1 :(得分:1)

严格来说,这是UB。但是,您始终可以将char *指针转换为uintptr_t(如果存在),然后安全地减去生成的整数。

void foo(std::vector<char>&v){
    v.push_back('a');
    auto front= uintptr_t (&v.front());
    auto back = uintptr_t (&v.back());
    size_t n1=back-front+1;
    v.push_back('b');//This could reallocate the vector elements
    size_t n2=back-front+1;
}

答案 2 :(得分:0)

这个特例是安全但丑陋和误导。

v.push_back('b');//This could reallocate the vector elements可能会导致您的容器重新分配。在这种情况下,下一行将使用不存在的frontback指针。即使是悬空指针,计算两个地址的差异也是安全的。什么是不安全的是取消引用它们。

正确的解决方案是使用vector::count()函数,它将始终保持同步。如果您(由于某种原因)不想打电话vector::count(),您应该使用++n1