While working on a submission, I found behaviour that I don't understand. I have three ways of populating my matrix from the input. One of them works, one of them compiles and runs but produces a slightly wrong result, and one of them crashes with a free(): invalid pointer
.
For simplicity, I leave the part about a wrong result out of this question and reduce the code to just reading input. Let's ignore the output for a moment and just consider the error messages for two test cases:
root@41d06f89ab19:/code# gp so1.cpp && ./so1.exe <evenmat/test1.in > so1.txt
root@41d06f89ab19:/code# gp so2.cpp && ./so2.exe <evenmat/test1.in > so2.txt
Segmentation fault (core dumped)
root@41d06f89ab19:/code# gp so3.cpp && ./so3.exe <evenmat/test1.in > so3.txt
root@41d06f89ab19:/code# gp so1.cpp && ./so1.exe <evenmat/test2.in > so1_2.txt
free(): invalid pointer
Aborted (core dumped)
root@41d06f89ab19:/code# gp so2.cpp && ./so2.exe <evenmat/test2.in > so2_2.txt
Segmentation fault (core dumped)
root@41d06f89ab19:/code# gp so3.cpp && ./so3.exe <evenmat/test2.in > so3_2.txt
Clearly, the behaviour of the three code snippets is not equivalent. Consider the three files below. How do their semantics differ? Why are they not all equivalent?
// File so1.cpp -
#include <iostream>
#include <vector>
#include <iterator>
#include <iomanip>
#include <cassert>
#include <algorithm>
#include <functional>
typedef std::vector<std::vector<int>> VVI;
typedef unsigned int uint;
int main () {
std::ios_base::sync_with_stdio(false);
int t; std::cin >> t;
for (int testcase = 0; testcase < t; testcase ++){
// num matrix rows
unsigned int n; std::cin >> n;
// populate matrix
VVI matrix; matrix.reserve(n);
//VVI matrix(n, std::vector<int>(n));
for(unsigned int row=0; row<n; row++){
for(unsigned int col=0; col<n; col++){
if(col==0){
// initialize row vector
//std::vector<int> myvec (n);
//matrix[row] = myvec;
matrix[row] = std::vector<int>(n); // free(): invalid pointer happens here
}
int el; std::cin >> el;
matrix[row][col]=el;
}
}
for (unsigned int i = 0; i < n; i++){
for (unsigned int j = 0; j < n; j++){
std::cout << matrix[i][j] << ' ';
}
std::cout << '\n';
}
std::cout << "\ntestcase " << testcase << ':' << std::endl;
}
}
// file so2.cpp
#include <iostream>
#include <vector>
#include <iterator>
#include <iomanip>
#include <cassert>
#include <algorithm>
#include <functional>
typedef std::vector<std::vector<int>> VVI;
typedef unsigned int uint;
int main () {
std::ios_base::sync_with_stdio(false);
int t; std::cin >> t;
for (int testcase = 0; testcase < t; testcase ++){
// num matrix rows
unsigned int n; std::cin >> n;
// populate matrix
VVI matrix; matrix.reserve(n);
//VVI matrix(n, std::vector<int>(n));
for(unsigned int row=0; row<n; row++){
for(unsigned int col=0; col<n; col++){
if(col==0){
// initialize row vector
std::vector<int> myvec (n);
matrix[row] = myvec; // segfault on this line
//matrix[row] = std::vector<int>(n);
}
int el; std::cin >> el;
matrix[row][col]=el;
}
}
}
}
// file so3.cpp
#include <iostream>
#include <vector>
#include <iterator>
#include <iomanip>
#include <cassert>
#include <algorithm>
#include <functional>
typedef std::vector<std::vector<int>> VVI;
typedef unsigned int uint;
int main () {
std::ios_base::sync_with_stdio(false);
int t; std::cin >> t;
for (int testcase = 0; testcase < t; testcase ++){
// num matrix rows
unsigned int n; std::cin >> n;
// populate matrix
//VVI matrix; matrix.reserve(n);
VVI matrix(n, std::vector<int>(n));
for(unsigned int row=0; row<n; row++){
for(unsigned int col=0; col<n; col++){
/*if(col==0){
// initialize row vector
//std::vector<int> myvec (n);
//matrix[row] = myvec;
matrix[row] = std::vector<int>(n);
}*/
int el; std::cin >> el;
matrix[row][col]=el;
}
}
for (unsigned int i = 0; i < n; i++){
for (unsigned int j = 0; j < n; j++){
std::cout << matrix[i][j] << ' ';
}
std::cout << '\n';
}
std::cout << "\ntestcase " << testcase << ':' << std::endl;
}
}
I'm hoping this is an easy question for c++ experts out there. Because if it is not, I'm really not sure how well I can minimalize the test cases.
The lines where free(): invalid pointer
and the segfault happen are indicated in the code snippets. Perhaps of note is that the stack in visual studio code shows that for so1.cpp
the free(): invalid pointer
with the second test input file happens within the following part of stl_vector.h
:
#if __cplusplus >= 201103L
/**
* @brief %Vector move assignment operator.
* @param __x A %vector of identical element and allocator types.
*
* The contents of @a __x are moved into this %vector (without copying,
* if the allocators permit it).
* Afterwards @a __x is a valid, but unspecified %vector.
*
* Whether the allocator is moved depends on the allocator traits.
*/
vector&
operator=(vector&& __x) noexcept(_Alloc_traits::_S_nothrow_move())
{
constexpr bool __move_storage =
_Alloc_traits::_S_propagate_on_move_assign()
|| _Alloc_traits::_S_always_equal();
_M_move_assign(std::move(__x), __bool_constant<__move_storage>());
return *this;
}
For reproducibility: