-2

(see edit) This is the minimal example of an issue I am having in a large chunk of code (see code below). I have a few vectors which are used to store input data. These vectors do not have a set size until some data is read in. Once the data is read in, the data is stored to the vectors, so I need to set the size of the vectors, fill the vectors, and then continue using the vectors for other purposes (which is why I thought of perhaps throwing them in a class). I had some test code that did all this just fine (similar to foo() below) and then moved it all over to my production code (similar to bar() below). However, bar() segfaults and I am unsure why. Is there a proper way to store a vector and reserve it/fill it later for use in a class like this?

#include <iostream>
#include <vector>

int VSIZE = 5;

class foobar {
public:
   foobar() { initialize(); };
   ~foobar() = default;

   static void foo();
   void bar();
private:
   void initialize() { bar(); };
   std::vector<std::vector<uint32_t>> vec1;
};

// This succeeds.
void foobar::foo() {
   std::vector<std::vector<uint32_t>> vec2;
   vec2.reserve(VSIZE);
   for(int i=0;i<VSIZE;i++){
      vec2[i].emplace_back(0);
   }
   std::cout << "Finished foo." << std::endl;
}

// This fails.
void foobar::bar() {

   // Reserve the needed memory for vec1.
   vec1.reserve(VSIZE);
   for(int i=0;i<VSIZE;i++){

      // Reserve the needed memory for each vector within vec1.
      vec1[i].reserve(VSIZE);
   }
   for(int i=0;i<VSIZE;i++){
      vec1[i].emplace_back(0);
   }
   std::cout << "Finished bar." << std::endl;
}

int main() {
   foobar::foo();
   foobar TC;
}

The output of this code is

Finished foo.
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)

EDIT: Okay so... I'll have to work out a better minimal example because I just realized based on the comments that replacing all the reserve's with resize work perfectly in this code. They DO NOT, however, work in my production code, because I've already tried that and just did again. I'll have to figure out what is different and modify the question further.

Solution

EDIT 2: New day at work and after reading the comments here I managed to fix my issue. I left all of the reserve's in except for my multidimensional vectors. By first using resize on those vectors and then further reserving their contained vectors my problem was solved. So for the above case I modified bar to be

void foobar::bar() {
   // Resize vec1 to allocate the needed storage.
   vec1.resize(VSIZE);
   for(int i=0;i<VSIZE;i++){

      // Reserve the needed memory for each vector within vec1.
      vec1[i].reserve(VSIZE);
   }
   for(int i=0;i<VSIZE;i++){
      vec1[i].emplace_back(0);
   }
   std::cout << "Finished bar." << std::endl;
}
d0sag3
  • 13
  • 3
  • 1
    [`reserve()`](https://en.cppreference.com/w/cpp/container/vector/reserve) doesn't change the size of vector, so accessing `vec2[0]` is out of bounds. It only pre-allocates some memory, so that `push_back` would be cheap. You should use [`resize()`](https://en.cppreference.com/w/cpp/container/vector/resize), which actually changes the size and you have the exact number of elements that you specified. – Yksisarvinen Mar 03 '20 at 22:45
  • 2
    Does this answer your question? [Choice between vector::resize() and vector::reserve()](https://stackoverflow.com/questions/7397768/choice-between-vectorresize-and-vectorreserve) – Yksisarvinen Mar 03 '20 at 22:46
  • @Yksisarvinen I am still wondering why this causes a `SIGSEGV`. I always thought that this only occurs when you access memory that is not owned by the object/program. But in this case, the vector already owns the memory and I would rather expect an exception by the vector. – wychmaster Mar 03 '20 at 22:55
  • @wychmaster • the memory is allocated, but no object in that memory has yet been constructed. – Eljay Mar 03 '20 at 23:00
  • @wychmaster you get a `SIGSEGV` because that's one possibility that can [result from demons flying out of your nose](https://en.wiktionary.org/wiki/nasal_demon). – Sam Varshavchik Mar 03 '20 at 23:00
  • @wychmaster the behaviour of undefined behaviour is undefined. – user4581301 Mar 03 '20 at 23:00
  • 2
    @wychmaster I suspect it's because it's vector of vectors. After `reserve` on the top vector, you own memory needed, true, but that's rubbish memory. This rubbish memory is interpreted as vectors (so basically a couple of pointers). If the memory for pointers is non-null, `emplace_back()` will assume memory was reserved and will construct object in that memory. Since pointers are rubbish, you actually try to write memory you don't own. But, as everyone else already said, undefined behaviour is undefined. – Yksisarvinen Mar 03 '20 at 23:01
  • The routine labelled `// This succeeds.` fails on my machine (crashes the process). – Eljay Mar 03 '20 at 23:05
  • Thank you guys for clearing that up ;) – wychmaster Mar 03 '20 at 23:05

1 Answers1

0

The crash is happening here:

#0  0x0000555555556352 in __gnu_cxx::new_allocator<unsigned int>::construct<unsigned int, int> (this=0x55555556deb0, __p=0x0) at /usr/include/c++/9/ext/new_allocator.h:147
#1  0x0000555555555cd7 in std::allocator_traits<std::allocator<unsigned int> >::construct<unsigned int, int> (__a=..., __p=0x0) at /usr/include/c++/9/bits/alloc_traits.h:484
#2  0x000055555555582e in std::vector<unsigned int, std::allocator<unsigned int> >::emplace_back<int> (this=0x55555556deb0) at /usr/include/c++/9/bits/vector.tcc:115
#3  0x0000555555555367 in foobar::bar (this=0x7fffffffdcc0) at foo.cc:35
#4  0x00005555555554e8 in foobar::initialize (this=0x7fffffffdcc0) at foo.cc:14
#5  0x00005555555554ad in foobar::foobar (this=0x7fffffffdcc0) at foo.cc:8
#6  0x00005555555553b1 in main () at foo.cc:42

In frame #2:

#2  0x000055555555582e in std::vector<unsigned int, std::allocator<unsigned int> >::emplace_back<int> (this=0x55555556deb0) at /usr/include/c++/9/bits/vector.tcc:115
115         _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,

(gdb) p this->_M_impl._M_finish
$1 = (std::_Vector_base<unsigned int, std::allocator<unsigned int> >::pointer) 0x0

And that is your answer: even though the storage for elements of vec1[] is reserved, none of the elements have been constructed yet.

Your code is equivalent to:

std::vector<int> *v = (std::vector<int> *) malloc(sizeof(*v));
v->emplace_back(0);

and it should be obvious why that doesn't work.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362