David Lougheed
David Lougheed

Reputation: 79

Error with vector operator= when copying vectors in C++

In my program, I am copying a vector<vector<int> > to another as such:

#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;
}

This, after around 3 minutes of running the program (which every once in a while calls this function) results in a memory access error. Valgrind gives me the following information about this function: ==31557== Invalid write of size 4 and Address 0x10207e360 is 0 bytes after a block of size 16 alloc'd

If I disable the function and do not call it, the program appears not to crash. GDB gives me malloc: *** error for object 0x1068a6878: incorrect checksum for freed object - object was probably modified after being freed.. The backtrace shows that it comes from 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

I think this is resulting in other errors as well, though I am not sure.

EDIT:

Here is my Tick() function which is also apparently contributing to this error.

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;

}

Upvotes: 2

Views: 741

Answers (2)

sehe
sehe

Reputation: 392954

Actually this might be the problem:

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

Did you mean

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

?

This would roughly match the last complaint by valgrind: invalid write at line ~(207-188) == ~+19 inside mix_genome.

Here's the relevant part decrufted:

==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)

OLD ANSWER TEXT

Although it's currently unknown whether this could still be relevant, the original analysis also showed other ways how you can "read" valgrind's diagnostics:

Your problem is not with the vectors. It looks like you have a stale reference (potentially a threading bug).

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

What this tells me is that in Tick() you're updating some kind of stats (including the FPS that gets printed on the console?). Apparently, this refers to an address:

==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)

So, likely it holds a reference to something that got freed/reallocated.

Note that when vectors resize, they can invalidate all existing references, pointers and/or iterators. To avoid this,

  • vector::reserve can be used to avoid having to reallocate on expected growth
  • you can use vector indices
  • you could look at boost::stable_vector

Edit Indeed mix_genome appears to write invalid addresses too. Reading more of your valgrind log.

Upvotes: 3

Kobotan
Kobotan

Reputation: 112

In this cicle

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

if i > gbuilt.size(), you must to use gbuilt.push_back(g2[i]). In other way not allocated memory for this item.

Upvotes: 0

Related Questions