views:

872

answers:

9

I'm trying to make a pointer point to a 2D array of pointers. what is the syntax and how would i access elements.

+1  A: 
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;
Hans Passant
thats only a pointer pointing to a 1D array of pointers.
TheFuzz
Maybe the array[row][col] syntax clarifies it?
Hans Passant
But it's still not contiguous.
Crashworks
@Crashworks: There was no requirement for the data to be contiguous.
Martin York
@nobugz: Still a very C like solution. The array should at least clean up itself.
Martin York
@Martin, well, the standard specifies a multidimensional array as contiguous (8.3.4). So, the requirement depends on what he meant by "2D array": if he means what the C++ standard calls a 2D array, then yes, it must be contiguous. If he just means something that has two subscripts, then heck, just use a `vector<vector<int *> >`.
Crashworks
@Crashworks: Admittedly multidimensional is an overloaded term. But few people use the C standard as a definition. But the question is quite specific on requirements (non of which require contiguous elements). Yes Vector<Vector> is a solution assuming Jagged arrays are fine.
Martin York
A: 

You can define a vector of vectors:

typedef my_type *my_pointer;

typedef vector<vector<my_pointer> > my_pointer2D;

Than create a class derived from my_pointer2D, like:

class PointersField: public my_pointer2D
{
  PointsField(int n, int m)
  {
     // Resize vectors....
  }
}

PointsField pf(10,10); // Will create a 10x10 matrix of my_pointer
Elalfer
point != pointer.
Georg Fritzsche
ahh, whatever...
Elalfer
Also, you shouldn't inherit from STL containers
Charles Salvia
@Charles: Shouldn't is too strong. Probably not a good idea. As the container does not have a virtual destructor. But under the write conditions (not these here) inheriting could be acceptable.
Martin York
+4  A: 

By the letter of the law, here's how to do it:

// 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;
  }
}

Be careful to delete the contained pointers, the row arrays, and the column array all separately and in the correct order.

However, more frequently in C++ you'd create a class that internally managed a 1D array of pointers and overload the function call operator to provide 2D indexing. That way you're really have a contiguous array of pointers, rather than an array of arrays of pointers.

Drew Hall
That's more like a pointer to an array of pointers to arrays.
Crashworks
@Crahsworks: So. If it quacks like a duck, is a duck.
Martin York
It depends on what OP means by "2d array". If he simply means something that can be accessed with two subscript operators `a[n][m]`, then yes, this is duck-typed as a multidim array. If he means what the standard specifies as a 2D array, which is a contiguous region of n*m many elements, then a 1D array of pointers to 1D arrays is not the same thing.
Crashworks
@Martin York: First, the NULL assignment loop was for initialization (so we don't have a bunch of garbage pointers)--I wasn't trying to show deallocation, but mentioned in the text how to go about it. I also mentioned in my text that you'd usually wrap it in a class which would provide RAII (what I meant by "internally managed"), but I was specifically just trying to answer the direct question. I didn't touch the more metaphysical "should you even try to do this" issue... :)
Drew Hall
@Crashworks: The contiguous m*n array was what I was trying to suggest in my last paragraph--in retrospect it doesn't quite read that way but that's what I intended (hence the need to overload operator()).
Drew Hall
@Drew: Sorry got the NULL part wrong. That would be great for the answer to a C question. But this is C++. I would expect to see resource management and auto-cleanup of the array itself using RAII. (Note the OP does not specify if the data in the array is owned so cleaning up of the contained pointers need not be done (but the array should be able to clean up itself)).
Martin York
@Crashworks: The question is quite clear on what is needed. There is no requirement of contiguous storage. Only the ability to declare and access elements.
Martin York
+2  A: 
int *pointerArray[X][Y];
int **ptrToPointerArray = pointerArray;

That's how you make a true (contiguous in memory) multidimensional array.

But realize that once you cast a multidimensional array to a pointer like that, you lose the ability to index it automatically. You would have to do the multidimensional part of the indexing manually:

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
Crashworks
C++ is strongly typed conversion from int* to int** is not allowed without an explicit cast.
Martin York
True. However, conversion from int*[] to int**, which is what I do here, is automatic.
Crashworks
True: However this is not that conversion: int **ptrToPointerArray = pointerArray; Try compiling your code with a C++ compiler and you will see the error.
Martin York
Good grief! Bjarne just loves to make trouble. I've made the correction.
Crashworks
A: 

See my code. It works on my FC9 x86_64 system:

#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: a response to "Isn't that an array of pointers to arrays?", adding the example of array of pointers, very simple:

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;
}

Did I still misunderstand your question?

And you could even

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;
}

It also works on my FC9 x86_64 system.

EffoStaff Effo
Isn't that an array of pointers to arrays?
Crashworks
@Crashworks. Again this still works like a 2D array.
Martin York
@Effo: This implementation has some serious bugs. Assignment operator missing. Possible double delete of pointers if used incorrectly. array_1 can be accidentally copied to a local copy and on desruction will delete a row in the array.
Martin York
@Martin York: right. I just write it to 1. present my ideas; 2. make sure the ideas will being work.
EffoStaff Effo
A: 

:)

I had these once in a piece of code I wrote.

I was the laughing stock of the team when the first bugs leaked out. On top of that we use Hungarian notation, leading to a name like papChannel - a pointer to an array of pointers...

It's not nice. It's nicer to use typedefs to define a 'row of columns' or vice versa. Makes indexing more clear, too.

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.
   }
}
xtofl
+1  A: 

Depends.
It can be as simple as:

int main()
{
    int*   data1[10][20]; // Fixed size known at compile time

    data1[2][3] = new int(4);
}

If you want dynamic sizes at runtime. You need to do some work.
But boost has you covered:

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

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

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

If you are fine with Jagged arrays that can grow dynamically:

int main()
{
    std::vector<std::vector<int*> >   data1;

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

Note: In all the above. I assume that the array does not own the pointer. Thus it has not been doing any management on the pointers it contains (though for brevity I have been using new int() in the examples). To do memory management correctly you need to do some more work.

Martin York
+2  A: 

You could try Boost::MultiArray.

Check out this page for details.

Chip
A: 

I prever to use the () operator. There are lots of reasons for this (c++ Faqs [13.10]). Change the internal representation to a std::vector if you like:

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];
};

You can use it like this:

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