【问题标题】:return 2D array in C++在 C++ 中返回二维数组
【发布时间】:2021-12-24 20:21:59
【问题描述】:

我是 C++ 的新手,我在 python 中进行物理模拟,这需要很长时间才能完成,所以我决定切换到 C++,但我不明白如何创建一个返回 2D 数组的函数(或 3D 数组)

#include <iostream>
#include <cmath>
// #include <complex> //

using namespace std;

double** psiinit(int L, int n, double alpha){
    double yj[400][400] = {};
    for (int j = 0; j < n; j++)
    {
        double xi[400] = {};
        for (int i = 0; i < n; i++)
        {
            xi[i] = exp(-(pow((i-(L/4)), 2) + (pow((j-(L/4)), 2)))/alpha) / (sqrt(2)*3.14159*alpha);
        };
        yj[j] = xi;
    };
    return yj;
}

int main(){
    int L = 10;
    int n = 400;
    int nt = 200*n;
    double alpha = 1;
    double m = 1;
    double hbar = 1;

    double x[n] = {};
    double y[n] = {};
    double t[nt] = {};

    double psi[nt][n][n] = {};
    psi[0] = psiinit(L, n, alpha);

    cout << psi <<endl;
    return 0;
}

我一直在寻找答案,但似乎不是针对我的问题

谢谢

【问题讨论】:

  • 创建一个代表二维数组的类。在该类中有一个线性 std::vector&lt;double&gt; 并将 x,y 位置转换为线性向量。返回那个 2D 类。
  • 你的数组是静态的,在栈上,只在psiinit里面定义。所以你不能退货。以您的方式返回它会将地址返回到在函数末尾停止存在的东西。为什么不写好 C++ 并使用std::vector?你真的是在用 C++ 编写 C。
  • double [400][400]double** 是不相关的类型。
  • 在建议的替代方案中:如果您仍然有固定大小,那么std::array&lt;std::array&lt;double, Columns&gt;, Rows&gt; 是不错的选择,您可以免费获得[row][column] 索引 - 但请注意,在定义数组数组时,您必须颠倒列和行的顺序。这是标准委员会决定(不幸?)在模板参数中将类型置于大小之前的结果——向量的向量带有(隐藏的)双指针间接寻址,每个元素访问以及多个数组分配,所以线性向量(见@Eljay)更胜一筹。
  • 您可能想要返回std::vector&lt;std::vector&lt;double&gt;&gt;。或者,因为你在那里有固定的尺寸,所以用std::unique_ptr&lt;std::array&lt;std::array&lt;double, 400&gt;, 400&gt;&gt; 使它更紧凑。使用(例如)auto yj = std::make_unique&lt;std::array&lt;std::array&lt;double, 400&gt;, 400&gt;&gt;(); 分配它,其余的应该很简单。您可以返回、传递和std::movestd::unique_ptr,它表示所有权并正确处理解除分配。对于范围受限的共享,您可以使用来自.get() 的原始指针。对于线程安全的引用计数共享,请使用std::shared_ptr

标签: c++ arrays multidimensional-array return


【解决方案1】:

您对数组、指针和返回值的理解是不完整的。我不能为你写一个关于这个主题的完整教程,但我建议你阅读这个。

同时,我建议您使用 std::vector 而不是 C 样式的数组,并将您的多维数组视为具有适当索引的一维向量,例如cell = vector[row * cols + col]

类似这样的:

#include <cmath>
// using std::exp, M_PI, M_SQRT2
#include <vector>

std::vector<double> psiinit(int L, int n, double alpha) {
    std::vector<double> yj(n * n);
    double div = M_SQRT2 * M_PI * alpha;
    for (int j = 0; j < n; j++)
    {
        double jval = j - L/4;
        jval = jval * jval;
        for (int i = 0; i < n; i++)
        {
            double ival = i - L/4;
            ival = ival * ival;
            yj[j * n + i] = std::exp(-(ival + jval) / alpha) / div;
        }
    }
    return yj;
}

附录:还有专门的库可以更好更快地支持矩阵。例如本征 https://eigen.tuxfamily.org/dox/GettingStarted.html

【讨论】:

    【解决方案2】:

    如果您是 C++ 新手,您应该阅读有关堆和堆栈的概念以及堆栈帧的内容。有很多很好的资源。

    简而言之,当你声明一个 C 风格的数组(例如yj)时,它是在函数的栈帧中创建的,因此一旦你退出栈帧,就不能保证它,你的程序当它引用返回的数组时调用未定义的行为。

    有 3 个选项:

    1. 将数组作为输出参数传递给函数(非常 C 风格,不推荐)。
    2. 将数组包装在一个类中(就像std::array 已经为您所做的那样),在这种情况下,它会保留在堆栈上,并在返回时复制到调用框架,但必须在编译时知道它的大小。
    3. 在堆上分配数组并返回它,在我看来这最适合您的情况。 std::vector 为您做到这一点:
    std::vector<std::vector<double>> psiinit(int L, int n, double alpha){
        std::vector<std::vector<double>> yj;
        for (int j = 0; j < n; j++)
        {
            std::vector<double> xi;
            for (int i = 0; i < n; i++)
            {
                const int value = exp(-(pow((i-(L/4)), 2) + (pow((j-(L/4)), 2)))/alpha) / (sqrt(2)*3.14159*alpha);
                xi.push_back(value);
            }
    
            yj.push_back(xi);
        }
        return yj;
    }
    

    如果您关心性能并且所有内部向量的大小都是固定的N,那么使用std::vector&lt;std::array&lt;double, N&gt;&gt; 可能会更好。

    【讨论】:

      【解决方案3】:

      堆分配和返回该指针也将起作用... 而不是

      double yj[400][400] = {}; 
      

      做,

      double** yj; 
      yj = new double*[400]; 
      yj[i] = new double[400];
      

      那么,

      return yj;
      

      【讨论】:

      • 这在 C 中是可以的,尽管分配一个紧凑的多维数组而不是 Java 样式的数组可能更好。在 C++ 中,我会使用更多的标准库,例如 std::array,更重要的是,std::unique_ptr
      【解决方案4】:

      或者像上面说的那样做一个包装器,或者使用一个向量的向量。

      #include <vector>
      #include <iostream>
      
      auto get_2d_array()
      {
          // use std::vector since it will allocate (the large amount of) data on the heap
          // construct a vector of 400 vectors with 400 doubles each
          std::vector<std::vector<double>> arr(400, std::vector<double>(400));
          arr[100][100] = 3.14;
          return arr;
      }
      
      int main()
      {
          auto arr = get_2d_array();
          std::cout << arr[100][100];
      }
      

      【讨论】:

      • std::array(因为大小是固定的)是一种替代方法。
      • @Jarod42 我考虑过,但如果大小大于 400,则堆栈空间可能存在一些问题。除此之外,这也是一个很好的解决方案
      • 考虑到如果大小不变,std::array 将是一个更好的选择,如果大小发生变化,std::vector 将是一个选择
      • 使用std::unique_ptr&lt;std::array&lt;std::array&lt;double, 400&gt;, 400&gt;&gt; 不会有堆栈空间问题。