【问题标题】:How do these three ways of creating vectors differ? [duplicate]这三种创建向量的方式有何不同? [复制]
【发布时间】: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++ 代码

标签: c++ vector c++14


【解决方案1】:

问题不在于您如何创建row,问题在于使用matrix[row] 您可以访问一个不存在的元素。

这里

    // populate matrix
    VVI matrix; matrix.reserve(n);

您没有填充矩阵。 reserve 只是为元素分配空间。它不会添加元素或更改向量大小。 matrix 的元素为零,并且在您的第一个代码中不会改变。在未定义的行为中越界访问向量。

如果你想拥有matrixn 行,你需要用n 元素初始化它:

VVI matrix{n};

VVI matrix;
matrix.resize(n);

或者...在您的第二个代码中,您使用大小为std::vector&lt;int&gt;s 的n 元素正确初始化matrix n

VVI matrix(n, std::vector<int>(n));

【讨论】:

  • 谢谢。如此明显的错误,但我和一些朋友花了一个小时。所以这意味着当matrix 已经作为正确大小的向量存在时,两个坏代码 sn-ps 将是等价的?我现在要问的是:不同的行为仅仅是由于未定义的行为,还是在含义上也有不同?
  • @lucidbrot 老实说我没有完全理解这个问题。您指的是 std::vector&lt;int&gt; myvec (n); matrix[row] = myvec;matrix[row] = std::vector&lt;int&gt;(n); 之间的区别吗?它们应该是相同的,但是在 UB 存在的情况下,一切都是相同的,而不是同时 ;)。明天你可能会得到另一个案例的段错误或根本没有段错误。
猜你喜欢
  • 2018-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-06
  • 1970-01-01
  • 2014-08-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多