【发布时间】:2020-09-22 06:58:15
【问题描述】:
在处理提交时,我发现了我不理解的行为。我有三种从输入中填充矩阵的方法。其中一个有效,其中一个编译并运行但产生了稍微错误的结果,其中一个崩溃并出现free(): invalid pointer。
为简单起见,我将有关错误结果的部分从这个问题中删除,并将代码简化为仅读取输入。让我们暂时忽略输出,只考虑两个测试用例的错误消息:
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
显然,三个代码 sn-ps 的行为是不等价的。 考虑下面的三个文件。它们的语义有何不同?为什么它们不都是等价的?
// 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;
}
}
我希望这对于 C++ 专家来说是一个简单的问题。因为如果不是,我真的不确定我能把测试用例最小化到什么程度。
free(): invalid pointer 和段错误发生的行在代码 sn-ps 中指示。或许值得注意的是,Visual Studio 代码中的堆栈显示,对于 so1.cpp,带有第二个测试输入文件的 free(): invalid pointer 发生在 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;
}
为了重现性:
【问题讨论】:
-
TL;博士。这:
VVI matrix; matrix.reserve(n);--reserve不会在向量中创建元素。 -
@PaulMcKenzie 我在编写此代码时就意识到了这一点,这就是为什么我要创建一个新向量以在第一次写入时分配给
matrix[row]。还是我没有做我认为我在做的事? -
是的,这是未定义的行为,不同的崩溃可能只是由于使用了移动或复制赋值运算符
-
@lucidbrot 未定义的行为未定义。试图弄清楚会发生什么是没有意义的。如果我在 Visual C++ 调试模式下运行你的代码,当你尝试访问
matrix[row]时,我可能会得到一个大的assert框,而使用另一个编译器或不同的编译器选项,结果会不同。 -
严格来说有两种可能的观点。您可以尝试了解为什么它在一种情况下会出现段错误,而在另一种情况下则不会,但您需要研究您正在使用的实现和编译器的输出。您只会深入了解针对此特定架构的特定编译器。另一种观点是:正确的 C++ 到处编译,我们不需要关心实现细节,你的代码不是正确的 C++ 代码