通过向量迭代(使用迭代器)会导致 SIGSEGV 但在索引中使用时有效 - 那怎么样?

Posted

技术标签:

【中文标题】通过向量迭代(使用迭代器)会导致 SIGSEGV 但在索引中使用时有效 - 那怎么样?【英文标题】:Iterating (with iterators) through vector causes SIGSEGV but works when using in indices - hows that? 【发布时间】:2015-03-02 12:02:00 【问题描述】:

我有一个向量,其中包含指向具有id() 函数的对象的指针:vector<*obj> vid() 返回一个整数,对对象来说是唯一的。在我的程序的某一时刻,我创建了六个 ID 为0,1,2,3,4,5 的对象。然后我打乱向量,因此指针的顺序不同:1,0,4,2,3,5

当我打印 id(以相反的顺序)时,我会收到 SIGSEGV 错误,具体取决于我访问向量的方式:

按索引:效果很好!

for(int i(v.size()-1); i>=0; --i)
        cout<<v.at(i)->id()<<"  "<<&(v.at(i))<<endl;

通过反向迭代器:访问最后一个元素时抛出错误:SIGSEGV

for(auto it(v.crbegin()); it!=v.crend(); ++it) //print in reverse order
        cout<<(*it)->id()<<"  "<<&(*it)<<endl;

先通过索引再通过 r-iterators 不会导致 SIGSEGV 但最后一个 id 是错误的 - 但地址是相同的:

for(int i(v.size()-1); i>=0; --i)
    cout<<v.at(i)->id()<<"  "<<&(v.at(i))<<endl;
cout<<endl;
for(auto it(v.crbegin()); it!=v.crend(); ++it)
    cout<<(*it)->id()<<"  "<<&(*it)<<endl;

打印:

1  0x1bbd708
0  0x1bbd700
4  0x1bbd6f8
2  0x1bbd6f0
3  0x1bbd6e8
5  0x1bbd6e0

1  0x1bbd708
0  0x1bbd700
4  0x1bbd6f8
2  0x1bbd6f0
3  0x1bbd6e8
0  0x1bbd6e0 ////should be 5!
//program continues!

所以我的问题是:为什么会这样,可能的原因是什么,为什么它使用索引而不是迭代器?还需要哪些额外信息?

编辑

我将其范围缩小到以下仍然具有该行为的代码。我创建了一个派对并让它有 4 个舞者(使用新的和原始的指针)。然后我得到我的指针向量的副本并让他们打印他们的 id,而如果我用迭代器访问他,第一个会失败。使用索引工作得很好。 (我知道缺少删除。)(这是 g++ 中的错误;我使用的是 4.9.1?)

#include <iostream>
#include <vector>
using namespace std;


class Dancer 
public:
    Dancer() : m_id(5+m_count++)   //add 5 to avoid having a 0
    int id() const   return m_id; 

private:
    static int m_count;
    const int m_id;
;
int Dancer::m_count=0;


class Party

public:
    Party()  
    void createDancer()  m_dancers.emplace_back(new Dancer()); 
    vector<Dancer*> dancers()  return m_dancers; 

private:
    vector<Dancer*> m_dancers;
;


int main() 
    Party party;
    for(uint i(0); i<4; ++i) //create 4 Dancers
        party.createDancer();

    //Print the id and address of the pointer
    for(unsigned int i(0); i<party.dancers().size(); ++i)  //using indecies
        cout<<party.dancers().at(i)->id()<<"  "<<&(party.dancers().at(i))<<endl;

    cout<<endl;

    for(auto it(party.dancers().cbegin()); it!=party.dancers().cend(); ++it) //using iterators
        cout<<(*it)->id()<<"  "<<&(*it)<<endl;

    return 0;

输出是:

5  0xb0d0c0
6  0xb0d0c8
7  0xb0d0d0
8  0xb0d0d8

0  0xb0d0c0 //iterator to the first element fails to get the correct id!
6  0xb0d0c8
7  0xb0d0d0
8  0xb0d0d8

【问题讨论】:

代码的其他部分可能存在未显示的错误,请提供MCVE 是的,我也有这种感觉,但是我的太循环或它们的顺序有什么区别? id() 是 const 并且只返回一个 const int。对象未修改... @dani 如果你在其他地方有未定义的行为也没关系。两段代码在正常情况下是等价的,在 UB 下可能会做完全不同的事情。 一个 mcve 意味着最少的代码,它本身会产生症状。它可能很难生成,但值得付出努力。要做的一件事是检查 id() 并查看该调用中的哪些内容与调试器有关。 你能详细说明“我洗牌向量”吗?如何? std::random_shuffle()?不洗牌还会出现问题吗 【参考方案1】:

我有一个带有指向对象指针的向量 [...]

不,你没有,你有很多很多不同的向量。

party.dancers() 按值返回,所以每次调用都会得到一个新对象。

您不能使用以下方法遍历该对象:

for(auto it(party.dancers().cbegin()); it!=party.dancers().cend()

因为it 迭代器指向的容器与您下次调用party.dancers() 时获得的容器不同。这是未定义的行为。

即使它有效,它也会非常低效,因为每次调用都会不断创建和销毁新向量,不必要地分配和释放内存。

(它也完全与您使用单个左值向量显示的原始代码不同:for(auto it(v.crbegin()); it!=v.crend(); ++it) 这就是为什么您应该始终显示真实代码而不是简化版本的原因您认为是等价的,但没有显示问题!

【讨论】:

以上是关于通过向量迭代(使用迭代器)会导致 SIGSEGV 但在索引中使用时有效 - 那怎么样?的主要内容,如果未能解决你的问题,请参考以下文章

通过迭代器访问另一个向量内的向量元素?

如何通过迭代器访问向量中的嵌套对?

向量迭代器不可取消引用 C++

访问算法的 lambda 函数中的迭代器导致我出现分段错误

STL迭代器之向量

使用向量迭代器时的 EXC_BAD_ACCESS?