Reputation: 29
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
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 ttt
s 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
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
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;
}
Upvotes: 2
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