如何在 C++ 中分配一个二维指针数组

Posted

技术标签:

【中文标题】如何在 C++ 中分配一个二维指针数组【英文标题】:How to allocate a 2D array of pointers in C++ 【发布时间】:2010-12-18 14:31:47 【问题描述】:

我正在尝试使指针指向二维指针数组。语法是什么?如何访问元素?

【问题讨论】:

是在编译时知道大小还是在运行时从固定大小创建?行和列是固定大小还是动态增长导致锯齿状数组?指针是属于数组还是只是持有?指针是否都相关(即相同的基类型)? 【参考方案1】:
double** array = new double*[rowCnt];
for (int row = 0; row < rowCnt; ++row)
  array[row] = new double[colCnt];
for (int row = 0; row < rowCnt; ++row)
  for (int col = 0; col < colCnt; ++col)
    array[row][col] = 0;

【讨论】:

也许 array[row][col] 语法可以澄清它? 但还是不连续。 @Crashworks:没有要求数据是连续的。 @nobugz:仍然是一个非常类似于 C 的解决方案。数组至少应该自行清理。 @Martin,好吧,标准将多维数组指定为连续的(8.3.4)。因此,要求取决于他所说的“二维数组”是什么意思:如果他的意思是 C++ 标准所说的二维数组,那么是的,它必须是连续的。如果他只是意味着有两个下标的东西,那么见鬼,只需使用vector&lt;vector&lt;int *&gt; &gt;【参考方案2】:

你可以定义一个向量的向量:

typedef my_type *my_pointer;

typedef vector<vector<my_pointer> > my_pointer2D;

比创建一个派生自 my_pointer2D 的类,如:

class PointersField: public my_pointer2D

  PointsField(int n, int m)
  
     // Resize vectors....
  


PointsField pf(10,10); // Will create a 10x10 matrix of my_pointer

【讨论】:

另外,你不应该从 STL 容器继承 @Charles:不应该太强。可能不是一个好主意。由于容器没有虚拟析构函数。但是在写入条件下(这里不是这些)继承是可以接受的。【参考方案3】:

按照法律规定,方法如下:

// Create 2D array of pointers:
int*** array2d = new (int**)[rows];
for (int i = 0; i < rows; ++i) 
  array2d[i] = new (int*)[cols];


// Null out the pointers contained in the array:
for (int i = 0; i < rows; ++i) 
  for (int j = 0; j < cols; ++j) 
    array2d[i][j] = NULL;
  

注意删除包含的指针、行数组和列数组,并按正确的顺序分别删除。

但是,在 C++ 中更常见的是创建一个在内部管理 1D 指针数组的类,并重载函数调用运算符以提供 2D 索引。这样你就真的有一个连续的指针数组,而不是一个指针数组。

【讨论】:

这更像是一个指向数组指针数组的指针。 这取决于 OP 所说的“二维数组”是什么意思。如果他只是意味着可以使用两个下标运算符a[n][m] 访问的东西,那么是的,这是鸭子类型的多维数组。如果他的意思是标准指定的 2D 数组,它是 n*m 多个元素的连续区域,那么指向 1D 数组的指针的 1D 数组就不是一回事了。 @Martin York:首先,NULL 赋值循环是为了初始化(所以我们没有一堆垃圾指针)——我不是要显示释放,而是在文中提到如何去做。我还在我的文章中提到,你通常会将它包装在一个提供 RAII 的类中(我的意思是“内部管理”),但我只是想回答直接的问题。我没有触及更形而上学的“你是否应该尝试这样做”的问题...... :) @Crashworks:连续的 m*n 数组是我在上一段中试图提出的建议——回想起来,它并不是这样读的,但这就是我的意图(因此需要重载运算符())。 @Drew:抱歉,NULL 部分弄错了。这对于回答 C 问题将非常有用。但这是 C++。我希望看到使用 RAII 对阵列本身进行资源管理和自动清理。 (请注意,OP 没有指定数组中的数据是否为所有,因此不需要清理包含的指针(但数组应该能够自行清理)。【参考方案4】:
int *pointerArray[X][Y];
int **ptrToPointerArray = pointerArray;

这就是你如何创建一个真正的(在内存中连续的)多维数组。

但是要意识到,一旦你将多维数组转换为这样的指针,你就失去了自动索引它的能力。您必须手动执行索引的多维部分:

int *pointerArray[8][6]; // declare array of pointers
int **ptrToPointerArray = &pointerArray[0][0]; // make a pointer to the array
int *foo = pointerArray[3][1]; // access one element in the array
int *bar = *(ptrToPointerArray + 3*8 + 1); // manually perform row-major indexing for 2d array

foo == bar; // true
int *baz = ptrToPointerArray[3][1]; // syntax error

【讨论】:

C++ 是从 int* 到 int** 的强类型转换,如果没有显式转换,则不允许。 是的。但是,我在这里所做的从 int*[] 到 int** 的转换是自动的。 True:但这不是转换: int **ptrToPointerArray = pointerArray;尝试使用 C++ 编译器编译您的代码,您将看到错误。 天哪! Bjarne 只是喜欢制造麻烦。我已经更正了。【参考方案5】:

查看我的代码。它适用于我的 FC9 x86_64 系统:

#include <stdio.h>

template<typename t>
struct array_2d 
        struct array_1d 
                t *array;
                array_1d(void)  array = 0; 
                ~array_1d()
                
                    if (array) 
                         delete[] array;
                         array = 0;
                    
                
                t &operator[](size_t index)  return array[index]; 
         *array;
        array_2d(void)  array = 0; 
        array_2d(array_2d<t> *a)  array = a->array; a->array = 0; 
        void init(size_t a, size_t b)
        
                array = new array_1d[a];
                for (size_t i = 0; i < a; i++) 
                        array[i].array = new t[b];
                
        
        ~array_2d()
        
                if (array) 
                        delete[] array;
                        array = 0;
                
        
        array_1d &operator[](size_t index)  return array[index]; 
;

int main(int argc, char **argv)

        array_2d<int> arr = new array_2d<int>;
        arr.init(16, 8);

        arr[8][2] = 18;
        printf("%d\n",
                arr[8][2]
                );

        return 0;

Effo UPD:对“这不是指向数组的指针数组吗?”的回应,添加指针数组的例子,很简单:

int main(int argc, char **argv)

        array_2d<int*> parr = new array_2d<int*>;
        int i = 10;
        parr.init(16, 8);
        parr[10][5] = &i;
        printf("%p %d\n",
                parr[10][5],
                parr[10][5][0]
                );

        return 0;

我还是误解了你的问题吗?

你甚至可以

typedef array_2d<int*> cell_type;
typedef array_2d<cell_type*> array_type;

int main(int argc, char **argv)

    array_type parr = new array_type;
    parr.init(16, 8);
    parr[10][5] = new cell_type;
    cell_type *cell = parr[10][5];
    cell->init(8, 16);
    int i = 10;
    (*cell)[2][2] = &i;

    printf("%p %d\n",
            (*cell)[2][2],
            (*cell)[2][2][0]
            );

    delete cell;

    return 0;

它也适用于我的 FC9 x86_64 系统。

【讨论】:

@Effo:这个实现有一些严重的错误。缺少赋值运算符。如果使用不正确,可能会双重删除指针。 array_1 可能会被意外复制到本地副本,并且在销毁时会删除数组中的一行。 @Martin York:对。我只是把它写到 1. 提出我的想法; 2. 确保这些想法能够奏效。【参考方案6】:

:)

我曾经在我编写的一段代码中使用过这些。

当第一个错误泄露时,我是团队的笑柄。最重要的是,我们使用匈牙利符号,导致类似papChannel 的名称 - 指向指针数组的指针...

这不好。使用 typedef 定义“列行”会更好,反之亦然。也使索引更加清晰。

typedef int Cell;
typedef Cell Row[30];
typedef Row Table[20];

Table * pTable = new Table;

for( Row* pRow = *pTable; pRow != *pTable+_countof(*pTable); ++pRow ) 
   for( Cell* pCell = *pRow; pCell != *pRow + _countof(*pRow); ++pCell ) 
     ... do something with cells.
   

【讨论】:

【参考方案7】:

这取决于。它可以很简单:

int main()

    int*   data[10][20]; // Fixed size known at compile time

    data[2][3] = new int(4);

如果您想要在运行时动态大小,您需要做一些工作。 但是Boost你有没有:

int main()

   int  x;
   int  y;
   getWidthAndHeight(x,y);

   // declare a 2D array of int*
   boost::multi_array<int*,2>   data(boost::extents[x][y]);

   data[2][3] = new int(6);

如果您对可以动态增长的jagged arrays 满意:

int main()

    std::vector<std::vector<int*> >   data;

    data.push_back(std::vector<int*>(10,NULL));
    data[0][3] = new int(7);

注意:以上所有内容。我假设数组不拥有指针。因此它没有对其包含的指针进行任何管理(尽管为简洁起见,我在示例中一直使用 new int() )。要正确进行内存管理,您需要做更多的工作。

【讨论】:

【参考方案8】:

你可以试试 Boost::MultiArray。

查看此page 了解详情。

【讨论】:

【参考方案9】:

我更喜欢使用 () 运算符。这有很多原因(C++ 常见问题解答13.10)。如果您愿意,可以将内部表示更改为 std::vector

template <class T, int WIDTH, int HIEGHT>
class Array2d

public:
    const T& operator ()(size_t col, size_t row) const
    
        // Assert col < WIDTH and row < HIEGHT
        return m_data [( row * WIDTH + col)];
    
    T& operator ()(size_t col, size_t row)
    
        // Assert col < WIDTH and row < HIEGHT
        return m_data [( row * WIDTH + col)];
    
private:
T m_data[WIDTH * HIEGHT];
;

你可以这样使用它:

Array2d< Object*, 10, 10 > myObjectArray;
myObjectArray(5,6) = new Object();

【讨论】:

以上是关于如何在 C++ 中分配一个二维指针数组的主要内容,如果未能解决你的问题,请参考以下文章

C++:使用函数为二维数组分配内存时出错

在函数 C++ 中分配内存二维数组

如何使用 C++ 中的指针动态分配和解除分配二维数组?

在 C# 中传递 IntPtr 指针后,在非托管 C++ 代码中分配数组,编组类型

如何在 C++ 中初始化指向动态二维数组的指针 [关闭]

c++二维数组和二级指针