Activity solution[a][b];
...
Activity **mother = solution;
Я хочу преобразовать 2D-массив объектов в указатель на указатель. Как я могу это сделать,
Я искал его в google. однако я нашел только один пример массива измерений.
Activity solution[a][b];
...
Activity **mother = solution;
Я хочу преобразовать 2D-массив объектов в указатель на указатель. Как я могу это сделать,
Я искал его в google. однако я нашел только один пример массива измерений.
Простое преобразование вам не поможет. Нет никакой совместимости между типом 2D-массива и типом указателя на указатель. Такое преобразование не имеет смысла.
Если вам действительно нужно это сделать, вам нужно ввести дополнительный промежуточный массив "индекс строки", который позволит преодолеть разрыв между семантикой 2D-массива и семантикой-указателем на указатель
Activity solution[a][b];
Activity *solution_rows[a] = { solution[0], solution[1] /* and so on */ };
Activity **mother = solution_rows;
Теперь доступ к mother[i][j]
даст вам доступ к solution[i][j]
.
Причина, по которой вы можете сделать это для одномерных массивов, а не двумерных массивов, связана с тем, как фактические элементы массива хранятся в памяти. Для одномерных массивов все элементы сохраняются последовательно, поэтому выражение array[i]
эквивалентно выражению *(array + i)
. Как вы можете видеть, размер массива не требуется для выполнения операции индекса массива. Тем не менее, для двумерных массивов элементы хранятся в порядке "строка основных", что означает, что сначала сохраняются все элементы в нулевой строке, за которыми следуют элементы в первой строке, за которыми следуют элементы во второй строке и т.д. Поэтому выражение array[i][j]
эквивалентно *(array + (i * ROW_SIZE) + j)
, где ROW_SIZE
- количество элементов в каждой строке. Следовательно, размер строки массива необходим для выполнения операции индекса массива, и приведение переменной массива в указатель теряет эту информацию.
Я хочу преобразовать 2D-массив объектов в указатель на указатель. Как я могу это сделать?
Почему? Это потому, что интерфейс ожидает указатель на указатели?
Если это так, вам нужно создать новый массив, содержащий эти указатели.
Activity solution[a][b];
Activity* solutionPtrs[a];
for (int i = 0; i < a; ++i)
solutionPtrs[a] = solution[a];
Activity** mother = solutionPtrs;
Почему вы не можете просто отличить 2D-массив от T
до T**
? Ну, потому что они не имеют ничего общего друг с другом!
Вы можете отличить T[a]
до T*
, потому что вы получаете указатель на первый элемент массива.
Вы можете сделать это и с 2D-массивами, но если у вас есть T[a][b]
, то он распадается на (T[b])*
, потому что 2D-массив не является массивом указателей, это массив массивов.
Это С++! Все возможно! Но это С++, чтобы он требовал некоторого уровня понимания.
Для этого давайте начнем с простого примера из 2 одномерных массивов: char firstName[4] = { 'J', 'o', 'n', '\0' }
и char lastName[4] = { 'M', 'e', 'e', '\0' }
. Посмотрите на возможную схему памяти здесь:
+------------+-------+
| 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'
+------------+-------+
Учитывая этот макет памяти, если вы должны были сделать cout << firstName << ' ' << lastName
, вы получите:
0x76543210 0x76543214
Эти массивы - действительно просто указатель на их первый элемент! Это иллюстрирует Array to Pointer Decay, о котором вы можете прочитать здесь: http://en.cppreference.com/w/cpp/language/array#Array-to-pointer_decay
Прежде чем двигаться дальше, здесь важно что-то важное, char
занимает ровно 1 байт, поэтому адрес каждого последующего char
в массиве будет просто следующим адресом. Это связано с Оператор подстроки таким образом: firstName[1]
эквивалентно *(firstName + 1)
. Это справедливо для char
, но также верно для любого другого типа, который занимает более 1 байта. Возьмем, например: short siArray = { 1, 2, 3, 4 }
, возможный макет памяти siArray
будет выглядеть так:
+------------+--------+
| Address | Value |
+------------+--------+
| 0x76543218 | 0x0001 | <- siArray[0] - 1
| 0x7654321A | 0x0002 | <- siArray[1] - 2
| 0x7654321C | 0x0003 | <- siArray[2] - 3
| 0x7654321E | 0x0004 | <- siArray[3] - 4
+------------+--------+
Даже если cout << siArray << ' ' << &(siArray[1])
выведет:
0x76543218 0x7654321A
*(siArray + 1)
будет по-прежнему индексировать один и тот же элемент siArray
как siArray[1]
. Это связано с тем, что при выполнении арифметики указателя С++ рассматривает тип адрес, при котором увеличивается значение short*
, фактически увеличит адрес на sizeof(short)
. Вы можете узнать больше об арифметике указателя здесь: http://en.cppreference.com/w/cpp/language/operator_arithmetic
Наконец, посмотрим, как С++ хранит 2-мерные массивы. Учитывая: char name[2][4] = { { 'J', 'o', 'n', '\0' }, { 'M', 'e', 'e', '\0' } }
возможный макет памяти:
+------------+-------+
| 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'
+------------+-------+
Поскольку мы знаем, что одномерное значение массива - это просто указатель, мы можем видеть из этого макета памяти, что name[0]
не является указателем, а просто первым символом первого массива. Таким образом, name
не содержит 2 1-мерных указателей массива, но содержит содержимое 2 массивов. (Кстати, на 32-битной машине, не сохраняющей указатели, сохраняется 8-байтная память, что довольно существенно для 8-байтового двумерного массива.) Таким образом, попытка обработки name
как char**
будет пытаться использовать символы как указатель.
Поняв это, нам просто нужно избегать использования С++ арифметика указателя, чтобы найти разыменование значения. Для этого нам нужно будет работать с char*
, так что добавление 1 действительно просто добавляет 1. Так, например:
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;
}
Обратите внимание, что в этом примере, хотя я ссылаюсь на si2DArray
мысль psi2DPointer
, я все еще использую информацию из si2DArray
для индексации, а именно:
size(si2DArray)
size(*si2DArray)
sizeof(*si2DArray)
sizeof(**si2DArray)
Таким образом, вы можете видеть, что потеря информации от преобразования из массива в указатель существенна. Возможно, у вас возникнет соблазн сохранить тип элемента, что также упростит арифметику указателя. Стоит отметить, что только преобразование в char*
считается определенным поведением reinterpret_cast
: http://en.cppreference.com/w/cpp/language/reinterpret_cast#Type_aliasing
Вы не можете. Они принципиально разные типы.
Не уверен, что вы искали что-то вроде этого. Вы должны предоставить более подробную информацию о том, чего вы хотите достичь. Это принципиально разные типы. Одно из решений - ниже.
Для записи, если кто-то сочтет это полезным:
// 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]);
}
}