conversion of 2D array to pointer-to-pointer

2020-01-23 07:48发布

问题:

Activity solution[a][b];

...

Activity **mother = solution;

I want to convert 2D array of objects to pointer-to-pointer. How can I do this;

I searched it on google. however I found only one dimension array example.

回答1:

A mere conversion won't help you here. There's no compatibility of any kind between 2D array type and pointer-to-pointer type. Such conversion would make no sense.

If you really really need to do that, you have to introduce an extra intermediate "row index" array, which will bridge the gap between 2D array semantics and pointer-to-pointer semantics

Activity solution[a][b];

Activity *solution_rows[a] = { solution[0], solution[1] /* and so on */ };

Activity **mother = solution_rows;

Now accessing mother[i][j] will give you access to solution[i][j].



回答2:

The reason you can do this for one-dimensional arrays and not two-dimensional arrays has to do with the way in which the actual array elements are stored in memory. For one-dimensional arrays, all of the elements are stored consecutively, so the expression array[i] is equivalent to the expression *(array + i). As you can see, the array size is not needed to perform an array index operation. However, for two-dimensional arrays, the elements are stored in "row major" order, meaning that all of the elements in the zeroth row are stored first, followed by the elements in the first row, followed by the elements in the second row, etc. Therefore, the expression array[i][j] is equivalent to *(array + (i * ROW_SIZE) + j), where ROW_SIZE is the number of elements in each row. Therefore, the array's row size is needed to perform an array index operation, and casting the array variable to a pointer loses that information.



回答3:

This is c++! Everything is possible! But a this is c++ so it requires some level of understanding.

To that end let's start with a simple example of 2 1-dimensional arrays: char firstName[4] = { 'J', 'o', 'n', '\0' } and char lastName[4] = { 'M', 'e', 'e', '\0' } Let's look at a possible memory layout here:

+------------+-------+
|  Address   | Value |
+------------+-------+
| 0x76543210 | 0x4A  | <- firstName[0] - 'J'
| 0x76543211 | 0x6F  | <- firstName[1] - 'o'
| 0x76543212 | 0x6E  | <- firstName[2] - 'n'
| 0x76543213 | 0x00  | <- firstName[3] - '\0'
+------------+-------+
| 0x76543214 | 0x4D  | <- lastName[0] - 'M'
| 0x76543215 | 0x65  | <- lastName[1] - 'e'
| 0x76543216 | 0x65  | <- lastName[2] - 'e'
| 0x76543217 | 0x00  | <- lastName[3] - '\0'
+------------+-------+

Given this memory layout if you were to do cout << firstName << ' ' << lastName you'd get:

0x76543210 0x76543214

These arrays are really just a pointer to their first element! This illustrates Array to Pointer Decay, which you can read more about here: http://en.cppreference.com/w/cpp/language/array#Array-to-pointer_decay

Before we move on there's something important here to note, chars take up exactly 1-byte so the address of each subsequent char in the array will simply be the next address. That's leveraged by the Subscript Operator in this way: firstName[1] is equivalent to *(firstName + 1). This is true for chars but is also true for any other type which takes up more than 1-byte. Let's take for example: short siArray = { 1, 2, 3, 4 }, a possible memory layout of siArray would look like:

+------------+--------+
|  Address   | Value  |
+------------+--------+
| 0x76543218 | 0x0001 | <- siArray[0] - 1
| 0x7654321A | 0x0002 | <- siArray[1] - 2
| 0x7654321C | 0x0003 | <- siArray[2] - 3
| 0x7654321E | 0x0004 | <- siArray[3] - 4
+------------+--------+

Even though cout << siArray << ' ' << &(siArray[1]) will output:

0x76543218 0x7654321A

*(siArray + 1) will still index the same element of siArray as siArray[1]. This is because when doing pointer arithmetic c++ considers the type of the address being operated on, thus incrementing a short* will actually increase the address by sizeof(short). You can read more about pointer arithmetic here: http://en.cppreference.com/w/cpp/language/operator_arithmetic

Lastly let's look at how c++ stores 2-dimensional arrays. Given: char name[2][4] = { { 'J', 'o', 'n', '\0' }, { 'M', 'e', 'e', '\0' } } a possible memory layout would be:

+------------+-------+
|  Address   | Value |
+------------+-------+
| 0x76543220 | 0x4A  | <- name[0][0] - 'J'
| 0x76543221 | 0x6F  | <- name[0][1] - 'o'
| 0x76543222 | 0x6E  | <- name[0][2] - 'n'
| 0x76543223 | 0x00  | <- name[0][3] - '\0'
| 0x76543224 | 0x4D  | <- name[1][0] - 'M'
| 0x76543225 | 0x65  | <- name[1][1] - 'e'
| 0x76543226 | 0x65  | <- name[1][2] - 'e'
| 0x76543227 | 0x00  | <- name[1][3] - '\0'
+------------+-------+

Since we know an 1-dimensional array value is really just a pointer, we can see from this memory layout that name[0] is not a pointer, it's just the first character of the first array. Thus name does not contain 2 1-dimensional array pointers, but contains the contents of the 2 arrays. (Incidentally on a 32-bit machine not storing the pointers saves 8-bytes of memory, which is pretty substantial for an 8-byte 2-dimensional array.) Thus trying to treat name as a char** would try to use the characters as a pointer.


Having understood this we really just need to avoid using c++'s pointer arithmetic to find dereference the value. To do that we'll need to work with a char* so that adding 1 is really just adding 1. So for example:

const short si2DArray[2][3] = { { 11, 12, 13 }, { 21, 22, 23 } };
const auto psi2DPointer = reinterpret_cast<const char*>(si2DArray);

for(auto i = 0U; i < size(si2DArray); ++i) {
    for(auto j = 0U; j < size(*si2DArray); ++j) {
        cout << *reinterpret_cast<const short*>(psi2DPointer + i * sizeof(*si2DArray) + j * sizeof(**si2DArray)) << '\t';
    }
    cout << endl;
}

Live Example

Note that in this example even though I reference si2DArray thought psi2DPointer I'm still using information from si2DArray to do the indexing, namely:

  1. How many arrays are in the major dimension: size(si2DArray)
  2. How many elements are in the minor dimension: size(*si2DArray)
  3. What is the size in memory of the minor dimension: sizeof(*si2DArray)
  4. What is the element type of the array: sizeof(**si2DArray)

You can thus see that the loss of information from converting from an array to a pointer is substantial. You may be tempted to preserve the element type, thereby also simplifying the pointer arithmetic. It's worthwhile to note that only a conversion to char* is considered defined behavior by reinterpret_cast: http://en.cppreference.com/w/cpp/language/reinterpret_cast#Type_aliasing



回答4:

I want to convert 2D array of objects to pointer-to-pointer. How can I do this?

Why? Is it because an interface expects a pointer to pointers?

If so, you'll need to create a new array that contains those pointers.

Activity solution[a][b];

Activity* solutionPtrs[a];
for (int i = 0; i < a; ++i)
    solutionPtrs[a] = solution[a];

Activity** mother = solutionPtrs;

Why can't you just cast a 2D array of T to T**? Well, because they have nothing to do with one another!

You can cast a T[a] to a T* because you get a pointer to the first element of the array.

You can do this with 2D arrays as well, but if you have a T[a][b] then it decays to a (T[b])* because a 2D array is not an array of pointers, it's an array of arrays.



回答5:

You can't. They are fundamentally different types.



回答6:

Not sure if you were looking for something like this. You should provide more details about what you want to achieve. They are fundamentally different types. One solution is to below.

For the record, if someone finds it useful:

// define matrix
double A[3][3] = {
    { 1, 2, 3},
    { 4, 5, 6},
    { 7, 8, 9}
};

// allocate memory
double ** A_ptr = (double **) malloc(sizeof (double *) * 3);
for (int i = 0; i < 3; i++)
    A_ptr[i] = (double *) malloc(sizeof (double) * 3);

// copy matrix
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        A_ptr[i][j] = A[i][j];
        printf(" %f ", A_ptr[i][j]);
    }
}