Tae-Rae Kim
Tae-Rae Kim

Reputation: 29

C++ Putting a string into a structure of vectors of strings

I have a problem in putting string value into a "structure of vectors of strings". The simplest reproducible code is as follows:

#include <vector>
#include <string>
#include <iostream>

using namespace std;

struct ttt {
  string name;
  unsigned int ID;
  vector<unsigned int> numList;
};

int main() {
  vector<ttt> b;
  b.reserve(3);
  b[0].ID = 1;
  b[0].numList.push_back(3);
  b[0].numList.push_back(4);
  string dd ("Desk");
  b[0].name = dd;
  cout << b[0].ID << b[0].name << b[0].numList[2] << endl;

  return 0;
}

The code compiles, but it failed to put "Desk" string into b[0].name, a structure element. Segmentation Fault arose at the spot.

I also tried below lines but all of them failed.

b[0].name.push_back(dd);
b[0].name += dd;

My compiler is GCC g++ 4.7.7 20120313, and I used below compile command.

/usr/bin/g++ --std=gnu++0x -Werror -Wall -Wextra -Warray-bounds

Any help would be deeply appreciated, sincerely.

Upvotes: 1

Views: 464

Answers (4)

The Eyesight Dim
The Eyesight Dim

Reputation: 78

when you call reserve() on a vector, it doesn't create any instances of the contained type. It only allocates the space for the elements.. So when you try to access these locations in the vector, you get undefined behaviour. You must push elements into the vector first or zero-initialize them with a call like vector<ttt> b(6); before trying to do any read or write on them.

Editing just the one line where you declare the vector of ttts and removing the reserve() call fixes this program.

Also beware, because you try to access b[0].numList[2] , the third element, but you only did push_back for two elements.

#include <vector>
#include <string>
#include <iostream>

using namespace std;

struct ttt {
  string name;
  unsigned int ID;
  vector<unsigned int> numList;
};

int main() {
  vector<ttt> b(3); //create 3 zero-init elements in vector b
  b[0].ID = 1;
  b[0].numList.push_back(3);
  b[0].numList.push_back(4);
  string dd ("Desk");
  b[0].name = dd;
  cout << b[0].ID << b[0].name << b[0].numList[2] << endl;
                                             //^beware, this hasn't been initialized
  return 0;
}

Output: 1Desk0

Upvotes: 0

Toby Speight
Toby Speight

Reputation: 30834

The first error reported by Valgrind on that code is

==28307== Conditional jump or move depends on uninitialised value(s)
==28307==    at 0x40154F: void std::vector<unsigned int, std::allocator<unsigned int> >::emplace_back<unsigned int>(unsigned int&&) (vector.tcc:94)
==28307==    by 0x4012D7: std::vector<unsigned int, std::allocator<unsigned int> >::push_back(unsigned int&&) (stl_vector.h:933)
==28307==    by 0x400F00: main (39273136.cpp:17)

Whilst this might seem a bit cryptic, a bit of experience suggests to check whethe the this argument to push_back() is initialised. Looking through the code, we see:

  vector<ttt> b;
  b.reserve(3);
  b[0].ID = 1;
  b[0].numList.push_back(3);

You've told the vector to prepare to have 3 elements, but then you never add any ttt objects to it. When you access b[0], you're using uninitialised memory (Valgrind doesn't complain about the assignment to b[0].ID because the memory has been allocated and belongs to b - but calling push_back attempts to read vector members that may be random garbage).

The obvious solution is to emplace_back() (or otherwise create) the elements of b.

Upvotes: 0

TemplateRex
TemplateRex

Reputation: 70526

There are two errors:

Assigning b[0] directly without calling push_back or without initializing it in a constructor call beforehand.

Another offending line is

b[0].numList[2]

because you only have called push_back() twice, and indexing is 0-based.

It would be much better to initialize the vector directly like this:

#include <string>
#include <vector>
#include <iostream>

using namespace std;

struct ttt {
  string name;
  unsigned int ID;
  vector<unsigned int> numList;
};

int main() {
  vector<ttt> b{{"Desk", 1, { 3, 4 }}};
  cout << b[0].ID << b[0].name << b[0].numList[1] << endl;
}

Live Example

Upvotes: 2

Vlad from Moscow
Vlad from Moscow

Reputation: 310980

You may not use the subscript operator for an empty vector to assign new values to it. Use instead push_back member function.

For example

std::vector<ttt> b;
b.reserve( 3 );

//...

b.push_back( ttt() );
b.back().ID = 1;
//...

Upvotes: 1

Related Questions