在C++中以运行时确定的大小表示二维数组的最佳方法

20

在C++中,我想做类似这样的事情:

int n = get_int_from_user();

char* matrix = new char[n][n];

matrix[0][0] = 'c';
//...
matrix[n][n] = 'a';

delete [][] matrix;

但是显然这样做是行不通的。那么有没有更好的方式实现类似的功能呢?我看过一些解决方案,但它们似乎非常混乱。


boost::multi_array 手动实现会很混乱。 - Dustin Getz
8个回答

26

手动动态方式:

假设您想要一个宽度为*高度的数组,最有效的方式是只使用单维数组:

char *matrix = new char[width*height];

要删除它:

delete[] matrix;

访问它的方法:

char getArrayValue(char *matrix, int row, int col)
{
  return matrix[row + col*width];
}

修改它:

void setArrayValue(char *matrix, int row, int col, char val)
{
  matrix[row + col*width] = val;
}

Boost矩阵:

如果您可以接受依赖关系,请考虑使用boost::matrix。

然后,您可以与boost线性代数库进行连接。

这里是一些boost::matrix的示例代码:

#include <boost/numeric/ublas/matrix.hpp>
using namespace boost::numeric::ublas;
matrix<char> m (3, 3);
for (unsigned i = 0; i < m.size1 (); ++ i)
    for (unsigned j = 0; j < m.size2 (); ++ j)
        m (i, j) = 3 * i + j;

在一些编译器上使用栈:

一些编译器实际上允许您在栈上创建具有运行时确定大小的数组。g++是这样一个编译器的例子。但是,VC++默认情况下不能这样做。

因此,在g++中,以下代码是有效的:

int width = 10;
int height = 10; 
int matrix[width][height];

Drew Hall提到,这个C99的特性叫做可变长数组(Variable Length Arrays,VLAs),在任何现代编译器中都可以打开。


请看问题标题,“...大小在运行时确定”。因此,他不能使用堆栈或常量。 - Paige Ruten
你可以在g++中在堆栈上创建大小确定的数组。 - Brian R. Bondy
这是C99的一个特性,叫做可变长度数组(VLAs)--它可以在任何支持C和C++的现代编译器中启用。 - Drew Hall

9

我通常会这样做:

char *matrix = new char [width * height];

matrix[i + j * width] = 'c'; // same as matrix[i][j] = 'c';

delete [] matrix;

是的,那可能是更好的方法。将访问封装在一个函数中,这样就非常不错了。 - Bernard
我相信这就是多维数组在底层处理的方式。 - Kip

5
您似乎没有理解 C++(C with classes)的全部意义 :-). 这种情况非常适合使用类来实现。
您可以使用 STL 或其他第三方类库,我相信它们会有您需要的数据结构,但如果您需要自己编写,请创建一个具有以下属性的类。
- 构造函数,给定 n,将创建一个新的 n*n 的 char 数组(例如 charray)。 - 成员函数根据 x.y 获取和设置值,这些值只是 charray[x*n+y]。 - 析构函数删除数组。

4

那么 std::vector< std::vector<int> > array2d; 呢?


4
值得注意的是性能特征。在一个方向上调整数组大小将会很快。但由于缓存局部性不好(向量可能分散在内存的远处),元素访问会变慢。另一方面,如果你使用像其他大多数答案中一样的连续存储,你会有稍微慢一点的竖向调整大小,稍微快一点的横向调整大小,以及更好的缓存局部性。 - Stefan Monov

3

对于真正的二维数组:

int n = get_int_from_user();

char** matrix = new char*[n];
for (int i = 0; i < n; i++) {
    matrix[i] = new char[n];
}

// Operations on matrix.

for (int i = 0; i < n; i++) {
    delete [] matrix[i];
}
delete matrix;

就我个人而言,以下是我的一些想法。毫无疑问会有错误。然而,我认为其他人贴出的方法更加优雅。


3

我喜欢1维数组的方法(Brian R. Bondy的选定答案),扩展思路是将数据成员封装成类,这样就不需要单独跟踪宽度:

class Matrix
{
    int width;
    int height;
    char* data;
public:
    Matrix();
    Matrix(int width, int height);
    ~Matrix();

    char getArrayValue(int row, int col);
    void setArrayValue(int row, int col, char val);
}

实现是读者的练习。;)

两个潜在的改进。你可以将其变成一个模板类。并且你可以用operator()(int,int)替换getArrayValue和setArrayValue,返回一个引用。或者理想情况下,通过代理类来模拟[x][y]语法 - 但是我认为确保代理类总是被优化掉很困难。 - Stefan Monov

0

我觉得这个不错。

int n = get_int_from_user();

char **matrix=new (char*)[n];

for(int i=0;i<n;i++)
    matrix[i]=new char[n];

matrix[0][0] = 'c';
//...
matrix[n][n] = 'a';

for(int i=0;i<n;i++)
    delete []matrix;
delete []matrix;

0
std::vector<int> m;

然后在运行时调用m.resize()。

int* matrix = new int[w*h];

如果你想要做类似高斯消元的操作,你的矩阵应该是这样的。
int** matrix = new int*[h];
for(size_t i(0); i < h; ++i)
    matrix[i] = new int[w];

(在高斯消元中,我们通常需要交换一行与另一行,因此最好在常数时间内交换指向行的指针,而不是在线性时间内进行复制交换。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接