【问题标题】:Error with vector operator= when copying vectors in C++在 C++ 中复制向量时向量运算符 = 出错
【发布时间】:2014-01-04 23:18:41
【问题描述】:

在我的程序中,我将vector<vector<int> > 复制到另一个这样的:

#include <vector>
#include <cstdlib>
#include <cmath>

typedef std::vector<std::vector<int> > VVector;

VVector mix_genome(VVector mix_genome,
    VVector g1,
    VVector g2,
    int gene_length)
{
    VVector gbuilt = g1; // valgrind gets angry at this a bit...

    for(int i = 0; i < 30; i++)
    {
        int syngamete_chance = std::floor(std::rand() % 100);
        if(syngamete_chance <= 50)
        {
            gbuilt[i] = g2[i];
        }
    }

    int mutation_chance = floor(rand() % 100);

    if(mutation_chance <= 3)
    {
        int gene_num = floor(rand() % 30);
        int act_num = floor(rand() % gene_length+1);
        int rand_act = floor(rand() % 8);

        gbuilt[gene_num][act_num] = rand_act;
    }

    return gbuilt;
}

这在运行程序大约 3 分钟后(每隔一段时间调用此函数)会导致内存访问错误。 Valgrind 为我提供了有关此功能的以下信息:==31557== Invalid write of size 4Address 0x10207e360 is 0 bytes after a block of size 16 alloc'd

如果我禁用该函数并且不调用它,程序似乎不会崩溃。 GDB 给了我malloc: *** error for object 0x1068a6878: incorrect checksum for freed object - object was probably modified after being freed.。回溯表明它来自 operator=:

#13 0x000000010000f8d4 in std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > >::operator= (this=0x7fff5fbfe780, __x=@0x7fff5fbfeb50) at vector.tcc:140

我认为这也会导致其他错误,但我不确定。

编辑:

这是我的 Tick() 函数,它显然也是导致此错误的原因。

int Creature::Tick() {
if(!dead) {
    food--;
    timeLasted++;
    step++;
    if(step > maxStep) {
        step = 0;
    }
    if(cooldown > 0) {
        cooldown--;
    }
    if(xpos > 178) {
        events[EVENT_RIGHT_SEEN_EDGE_OF_SCREEN] = true;
    }
    else if(xpos < 20) {
        events[EVENT_LEFT_SEEN_EDGE_OF_SCREEN] = true;
    }

    if(ypos > 179) {
        events[EVENT_BOTTOM_SEEN_EDGE_OF_SCREEN] = true;
    }
    else if(ypos < 20) {
        events[EVENT_TOP_SEEN_EDGE_OF_SCREEN] = true;
    }
    events[EVENT_NOTHING_HAPPENED] = true;
    for(int z = 0; z < NUM_EVENTS-1; z++) {
        if(events[z]) {
            events[EVENT_NOTHING_HAPPENED] = false; // events[z] is true so something happened
        }
    }
    for(int i = 0; i < NUM_EVENTS-1; i++) { // last event should always be "nothing happened"
        if(events[i]) {
            Action(genome[i][step]); // this has been ided by valgrind: invalid read size 4
            events[i] = false;
        }
    }
    if(!fighting && events[EVENT_NOTHING_HAPPENED]) {
        Action(genome[EVENT_NOTHING_HAPPENED][step]);
    }
    if(food < 0)
        food = 0;
    if(food > maxFood)
        food = maxFood;
    if(food == 0) {
        lifetime -= 5;
    }
    if(xpos > 200-bodySize) {
        xpos = 200-bodySize;
    }
    else if(xpos < 0) {
        xpos = 0;
    }
    if(ypos > 200-bodySize) {
        ypos = 200-bodySize;
    }
    else if(ypos < 0) {
        ypos = 0;
    }
}
return timeLasted;

}

【问题讨论】:

  • valgrind 在源代码的哪一行显示错误?
  • 您可能在其他地方有堆损坏/未定义行为
  • @JamesMcLaughlin 它不显示函数代码本身,而是显示对函数的调用,即:new_genome = mix_genome(creatures[breedCreature1].genome, creatures[breedCreature2].genome, creatures[body].bodySize*2);
  • @sehe 我怎样才能知道如何/在哪里?编辑 - 感谢您提高代码可读性
  • 如果 valgrind 没有告诉你(我们不知道,因为你显示的信息太少),clang++ 有一个地址清理器/未定义的行为检测标志

标签: c++ vector operators


【解决方案1】:

其实可能是问题所在:

int act_num = floor(rand() % gene_length+1);

你是说

int act_num = rand() % (gene_length+1);

?

这大致符合 valgrind 的最后一次投诉:invalid write at line ~(207-188) == ~+19 inside mix_genome

下面是相关的部分:

==31557== Invalid write of size 4
==31557==    at 0x100001C43: mix_genome(...) (Game.h:207)

...

==31557==  Address 0x10207e360 is 0 bytes after a block of size 16 alloc'd
==31557==    by 0x10000CB10: std::vector<...>::vector(std::vector<...> const&) (stl_vector.h:233)
==31557==    by 0x100001AD3: mix_genome(std::vector<...>, ...) (Game.h:188)

旧答案文本

虽然目前尚不清楚这是否仍然相关,但原始分析还显示了如何“读取”valgrind 诊断的其他方式:

您的问题不在于向量。看起来您有一个过时的引用(可能是线程错误)。

FPS: 0    # of Creatures: 414    # of Food: 348      ==31557== Invalid read of size 4
==31557==    at 0x1000016BC: Creature::Tick() (Creature.h:204)

这告诉我的是,在Tick() 中,您正在更新某种统计数据(包括打印在控制台上的 FPS?)。显然,这是指一个地址:

==31557==  Address 0x100095980 is 0 bytes after a block of size 16 alloc'd
==31557==    at 0xC658: malloc (vg_replace_malloc.c:295)
...
(std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > > const&) (stl_vector.h:233)
==31557==    by 0x10000CCA2: Creature::Creature(Creature const&) (Creature.h:52)

因此,它可能包含对已释放/重新分配的某些内容的引用。

请注意,当向量调整大小时,它们会使所有现有的引用、指针和/或迭代器失效。为了避免这种情况,

  • vector::reserve 可用于避免重新分配预期增长
  • 您可以使用矢量索引
  • 你可以看看boost::stable_vector

编辑确实mix_genome 似乎也写入了无效地址。阅读更多您的 valgrind 日志。

【讨论】:

  • 可能是这样的:gbuilt[gene_num][act_num] = rand_act; 可能来自无效的 act_num?我想我的意思是地板,但不是指之后的+1。我会调查的。
  • 你知道,rand() % gene_length 在定义上是不可分割的。你不能再floor它了。
  • 好吧,这似乎不是问题。但是,我根本不应该将 1 添加到gene_length,所以还是感谢您指出这一点。
  • 我实际上并没有指出这一点。这可能是完全正确的。我希望您发现我在回答中尝试向您展示您如何从 valgrind 诊断中得出这一行。这是有用的部分。
  • 我已经恢复了旧的分析文本,因为即使现在不是实质性的,它也可以帮助你“阅读”valgrind 日志。
【解决方案2】:

在这篇文章中

for(int i = 0; i < 30; i++)
{
    int syngamete_chance = std::floor(std::rand() % 100);
    if(syngamete_chance <= 50)
    {
        gbuilt[i] = g2[i];
    }
}

如果 i > gbuilt.size(),则必须使用 gbuilt.push_back(g2[i])。 以其他方式未为此项目分配内存。

【讨论】:

  • 你怎么知道向量不够大?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-10
  • 2012-09-29
  • 1970-01-01
  • 2020-01-18
  • 2020-03-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多