4

I think it is a trivial question, but I couldn't find a specific solution to it. I'm trying to append array into a vector, using push_back() function. Here is the code:

int main()
{
  std::vector<int*> matchVector;

  int msmTemp[3];

  msmTemp[0] = 1;
  msmTemp[1] = 2;
  msmTemp[2] = 3;
  matchVector.push_back(msmTemp);
  msmTemp[0] = 4;
  msmTemp[1] = 7;
  msmTemp[2] = 0;
  matchVector.push_back(msmTemp);

  for(auto i : matchVector)
  {
    for(int j = 0; j<3; j++)
    {
      cout<<i[j]<<", ";
    }
    cout<<"\n";
  }

  return 0;
}

The output I'm getting is 4,7,0 two times. I don't understand as to why I'm not able to see the previous values, namely 1,2,3? Is it because of the type of vector matchVector defined above? I think it needs to be array only.

archity
  • 562
  • 3
  • 11
  • 22
  • What do you think the 2 `int*` in your vector refer to? The exact same array – UnholySheep Jul 13 '18 at 13:15
  • 2
    Arrays are not pointers. Besides, one can just do `v.insert(v.end(), std::begin(arr), std::end(arr))`. – Incomputable Jul 13 '18 at 13:16
  • Is there a reason why you are not using a `vector>`? – Oneiros Jul 13 '18 at 13:16
  • Is there a reason why you are not using `vector`? – Caleth Jul 13 '18 at 13:16
  • 2
    You save a pointer to a vector, change the values stored by that pointer and then save the same pointer to vector. After that you twice take the same pointer from vector and print values stored by that pointer – DvoryankinEvgeny Jul 13 '18 at 13:16
  • 1
    `matchVector` simply contains two pointers to `msmTemp`. So your result is just printing `msmTemp` twice. What's the value of `msmTemp` at the time you print your results? – François Andrieux Jul 13 '18 at 13:17
  • That's because you push just the pointer `msmTemp` onto the vector, twice. When you come to print the contents of the vector you will just print the current contents of `msmTemp`, twice, which will hold the last values stored there. – quamrana Jul 13 '18 at 13:17
  • It's because you push the same pointer twice. You have two pointers pointing to the same part of memory, meaning changing value for it, change it for both of them. – Cirrus Jul 13 '18 at 13:19
  • 'std::vector>' – keith Jul 13 '18 at 13:22

3 Answers3

8

A int* is a pointer to an integer.

An int[3] is an array of 3 integers.

An array of 3 integers "decays" at the drop of a hat to a pointer to the first element.

When you do push_back(msmTemp), you push a pointer to the first element of msmTemp into the vector.

Pointers in C++ do not own what they point to. The vector afte the two push_backs contains two pointers, both to the same array msmTemp.

When you later iterate over the vector, you get two pointers in turn. Each points to msmTemp.

You then use [] to index those pointers. When you have a pointer to the first element of an array, you can use [] to access the other elements of the array. [0] is the first element, [1] the second, etc.

So you look at the 3 elements in msmTemp (luckily it has 3) and look at them twice, because you have two pointers into it in the vector.

You can inject elements like this:

std::vector<int> matchVector;
int msmTemp[3];
msmTemp[0]={1};
msmTemp[1]={2};
msmTemp[2]={3};
matchVector.insert( matchVector.end(), std::begin(msmTemp), std::end(msmTemp) );

etc. This ends up with a vector containing 6 elements, not two arrays.

If you want arrays as values you need std::array:

std::vector< std::array<int,3> > matchVector;
std::array<int, 3> msmTemp;

and then your code works as written. std::array is a library type that acts sort of like a raw array, but it doesn't have the decay-to-pointer problems of a raw array.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
2

Forget that int[3] names a type. C arrays don't behave like sensible values. Arrays are named std::array<type, count>.

#include <vector>
#include <array>

int main()
{
  std::vector<std::array<int, 3>> matchVector;

  std::array<int, 3> msmTemp;

  msmTemp[0] = 1;
  msmTemp[1] = 2;
  msmTemp[2] = 3;
  matchVector.push_back(msmTemp);
  msmTemp[0] = 4;
  msmTemp[1] = 7;
  msmTemp[2] = 0;
  matchVector.push_back(msmTemp);

  for(auto & arr : matchVector)
  {
    for(auto i : arr)
    {
      std::cout << i <<", ";
    }
    std::cout<<"\n";
  }

  return 0;
}
Caleth
  • 52,200
  • 2
  • 44
  • 75
  • The first line in the main function defines a vector which will hold values of type array, which in turn would be of type int of size 3, is my interpretation correct? – archity Jul 13 '18 at 13:32
  • 1
    @archity `std::array` is not, on it's own, a type. `std::array` is a type, which is an array of 3 `int`s – Caleth Jul 13 '18 at 13:33
  • @archity You read `matchVector` as "vector of array of 3 ints" – Caleth Jul 13 '18 at 13:34
  • Also, how do I break this statement into 2 parts, initialization and assignment, so that I can use it for any other varible rather than size 3? Especially, if I want to define it inside a class. – archity Jul 13 '18 at 14:06
  • 1
    To use `std::array`, you have to have a compile time constant for the count. If you don't have one, use `std::vector` – Caleth Jul 13 '18 at 14:08
  • You mean, I should use `std::vector>` ? – archity Jul 13 '18 at 14:11
  • What are you trying to achieve? Some sort of matrix class? – Caleth Jul 13 '18 at 14:13
  • Well yeah, there is a class which has a 2D array (matrix), whose number of columns (3 for this question) depend on value passed through the constructor of the class. – archity Jul 13 '18 at 14:16
  • Well I'd first recommend finding a linear algebra library, e.g. [Eigen](http://eigen.tuxfamily.org/dox/GettingStarted.html) or [Boost::ublas](https://www.boost.org/doc/libs/1_67_0/libs/numeric/ublas/doc/matrix.html), rather than reinventing the wheel – Caleth Jul 13 '18 at 14:19
  • I tried Eigen. The problem with library like Eigen is that it tends to make code execution considerably slower. Infact, even slower than MATLAB. Can you shed some light on the std::vector> thing, if possible? – archity Jul 16 '18 at 20:14
  • What don't you understand? Each element of a `vector>` is a `vector`, so you can access it as `mat[row][col]`. However that isn't contiguous storage of elements, so more often you'll wrap a `vector` with an index lookup `row * size + col` – Caleth Jul 16 '18 at 22:13
2

The other answers already explain how to fix your code. I think it's also good to explain why your code behaves the way it does:

Here you tell your compiler to create an std::vector that holds pointers to int:

std::vector<int*> matchVector;

Here you tell your compiler to allocate some space on the stack that fits 3 ints:

int msmTemp[3];

Here you tell your compiler to write the values 1, 2 and 3 into the memory previously allocated:

msmTemp[0] = 1;
msmTemp[1] = 2;
msmTemp[2] = 3;

Here you tell your compiler to take the address of that allocated space, treat it as a pointer and pass it to push_back. This is called array decaying:

matchVector.push_back(msmTemp);

Your matchVector now contains 1 element, which is a pointer to the address of the memory on your stack that was allocated to hold 3 ints.

Here you tell your compiler to write the values 4, 7 and 0 in the memory previously allocated. Note that this is still the same memory block as before:

msmTemp[0] = 4;
msmTemp[1] = 7;
msmTemp[2] = 0;

Here you tell your compiler to again take the address of the allocated space, treat it as a pointer and pass it to push_back:

matchVector.push_back(msmTemp);

Thus matchVector now contains 2 identical values, each a pointer to the same memory location. Specifically the memory location that you last wrote 4, 7 and 0 into.

Max Vollmer
  • 8,412
  • 9
  • 28
  • 43