【问题标题】:Set array dimension at runtime在运行时设置数组维度
【发布时间】:2016-05-21 05:34:48
【问题描述】:

我有一个结构,根据运行时的用户输入,它需要 1D 数组或 3D 数组。它永远不需要两者。现在,我像下面的示例代码一样设置了它,带有可以指向 1D 数组或 3D 数组的单独变量。我希望结构中只有一个变量可以指向 1D 数组或 3D 数组,其中维度是在运行时设置的。我有 C 的中级知识,并且是 C++ 的初学者。我愿意接受基于 C++ 概念的答案,但 与在迭代值时使用 C 相比没有减速(或减速可忽略不计)。如果它是一个 3D 数组,那么访问和更改数组值的 for 循环是我代码中的最大瓶颈。数组设置好后,我不需要更改数组的维度或大小。

有没有办法做到这一点,还是我应该满足于在我的结构中总是有一个无关的变量?

#include <iostream>
using namespace std;

typedef struct {
  int dim;
  int *one_d_arr;
  int ***three_d_arr;
} Struct;

int main() {
  int count = 0;
  int *arr1 = (int*) malloc(2 * sizeof(int));
  arr1[0] = 0;
  arr1[1] = 1;
  int ***arr3 = (int***) malloc(2 * sizeof(int**));
  for (int i=0; i<2; i++) {
    arr3[i] = (int**) malloc(2 * sizeof(int*));
    for (int j=0; j<2; j++) {
      arr3[i][j] = (int*) malloc(2 * sizeof(int));
      for (int k=0; k<2; k++) {
        arr3[i][j][k] = count++;
      }   
    }   
  }
  Struct s;
  s.one_d_arr = NULL;
  s.three_d_arr = NULL;
  cout << "Enter number of dimensions: ";
  cin >> s.dim;
  if (s.dim==1) {
    s.one_d_arr = arr1;
    cout << s.one_d_arr[0] << ", " << s.one_d_arr[1] << endl;
  }
  else if (s.dim==3) {
    s.three_d_arr = arr3;
    cout << s.three_d_arr[0][0][0] << ", " << s.three_d_arr[0][0][1] << endl;
    cout << s.three_d_arr[0][1][0] << ", " << s.three_d_arr[0][1][1] << endl;
    cout << s.three_d_arr[1][0][0] << ", " << s.three_d_arr[1][0][1] << endl;
    cout << s.three_d_arr[1][1][0] << ", " << s.three_d_arr[1][1][1] << endl;
  }
  else {
    cout << "Must enter 1 or 3" << endl;
  }
}

【问题讨论】:

  • 我想你可能想在 C++ 中使用new(而不是malloc)。
  • 指针不是数组!您的问题中没有 3D 数组。成为 C 或 C++ 的 3 星级程序员可不是恭维!
  • 在 C++ 中,变量的类型必须在编译时固定。数组的维度是其类型的一部分。所以从字面上看,答案是否定的。然而,实际上有很多方法可以解决这个问题。 (1) 始终将其设为一维数组(或者更好的向量可能!),并使用布尔标志来指示您应该如何处理/遍历它。 (2) 有两个不同的struct,获取数据时在它们之间进行选择。
  • 首先,使用您的类型的代码将在编译时决定如何使用它,但不同维度的语法不同。那么,如果您使用具有 1D 语法的 3D 数组会发生什么?由于它是同一类型,编译器将无法检测到这一点。我认为您的方法存在缺陷,也许退后一步并描述您的问题会有所帮助。此外,请阅读 C++ 教程。你的代码犯了一堆正确的学习应该能够避免的错误。
  • set array size at runtime == std::vector

标签: c++ arrays multidimensional-array struct


【解决方案1】:

我的建议是在这里使用两种不同的类型,而不是单个结构。使用抽象基类,您可以使两个子类都符合单个接口,但它们将具有不同的底层行为。一个非常基本的例子:

class ArrayBase {
  int dim;

  public:
    // This function is pure virtual, which means it's impossible to      
    // instantiate an instance of ArrayBase. Any class that inherits from
    // ArrayBase must implement printArray().
    virtual void printArray() = 0;
}

class Array1D : public ArrayBase {
  int* array;

  void printArray() {
    // some code to print this one-dimensional array
  }
}

class Array3D : public ArrayBase {
  int*** array;

  void printArray() {
    // some code to print this three-dimensional array
  }
}

以后需要用到数组的时候,可以动态分配你需要的类型,像这样:

ArrayBase* inputArray;

// if the user wants a 1D array
inputArray = new Array1D();

// if the user wants a 3D array
inputArray = new Array3D();

// this will call the appropriate function to print the array
inputArray->printArray(); 

如果你真的想要一个类型,使用 boost::any 是一种将你的两个数组指针压缩为一个的方法。我不会推荐这种方法,但它会起作用。

【讨论】:

  • 我接受了这个答案,因为它为我指明了重构代码的好方向。在实现类似的东西之前,我会花一些时间学习类和其他 C++ 容器。谢谢!
【解决方案2】:

关于 C/C++ 指针的有趣之处之一是 void 指针的存在。 void 指针可以指向你想要的任何东西,从 intint em> ***。 所以你可以简单地使用以下代码:

#define CAST1(arr) ((int *)arr)
#define CAST3(arr) ((int ***)arr)
#define CAST(arr,i) CAST##i(arr)

typedef struct {
    int dim;
    void *arr;
} Struct;

int main()
{
    Struct s;
    cin >> s.dim;
    int count = 0;

if (s.dim == 1){
    s.arr = malloc(2 * sizeof(int));
    CAST(s.arr, 1)[0] = 0;
    CAST(s.arr, 1)[1] = 1;
}
else if (s.dim == 3){
    s.arr = malloc(2 * sizeof(int ***));
    for (int i = 0; i < 2; i++){
        CAST(s.arr, 3)[i] = (int **) malloc(2 * sizeof(int **));
        for (int j = 0; j < 2; j++){
            CAST(s.arr, 3)[i][j] = (int *)malloc(2 * sizeof(int *));
            for (int k = 0; k < 2; k++){
                CAST(s.arr, 3)[i][j][k] = count++;
            }
        }
    }
}

if (s.dim == 1) {
    cout << CAST(s.arr, 1)[0] << ", " << CAST(s.arr, 1)[1] << endl;
}
else if (s.dim == 3) {
    cout << CAST(s.arr, 3)[0][0][0] << ", " << CAST(s.arr, 3)[0][0][1] << endl;
    cout << CAST(s.arr, 3)[0][1][0] << ", " << CAST(s.arr, 3)[0][1][1] << endl;
    cout << CAST(s.arr, 3)[1][0][0] << ", " << CAST(s.arr, 3)[1][0][1] << endl;
    cout << CAST(s.arr, 3)[1][1][0] << ", " << CAST(s.arr, 3)[1][1][1] << endl;
}
else {
    cout << "Must enter 1 or 3" << endl;
}

system("pause");

return 0;
}

【讨论】:

  • 谢谢,很高兴知道这一点。但是,基于 cmets,看起来最好的答案将是对我的编码概念进行更重大的改革。我会等待一个答案,告诉我如何清理我的代码,但如果没有出现,我会选择这个。
猜你喜欢
  • 1970-01-01
  • 2017-05-13
  • 2021-05-19
  • 2023-03-11
  • 2012-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-07
相关资源
最近更新 更多