Подтвердить что ты не робот

Преобразование двумерного массива в указатель на указатель

Activity solution[a][b];

...

Activity **mother = solution;

Я хочу преобразовать 2D-массив объектов в указатель на указатель. Как я могу это сделать,

Я искал его в google. однако я нашел только один пример массива измерений.

4b9b3361

Ответ 1

Простое преобразование вам не поможет. Нет никакой совместимости между типом 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].

Ответ 2

Причина, по которой вы можете сделать это для одномерных массивов, а не двумерных массивов, связана с тем, как фактические элементы массива хранятся в памяти. Для одномерных массивов все элементы сохраняются последовательно, поэтому выражение array[i] эквивалентно выражению *(array + i). Как вы можете видеть, размер массива не требуется для выполнения операции индекса массива. Тем не менее, для двумерных массивов элементы хранятся в порядке "строка основных", что означает, что сначала сохраняются все элементы в нулевой строке, за которыми следуют элементы в первой строке, за которыми следуют элементы во второй строке и т.д. Поэтому выражение array[i][j] эквивалентно *(array + (i * ROW_SIZE) + j), где ROW_SIZE - количество элементов в каждой строке. Следовательно, размер строки массива необходим для выполнения операции индекса массива, и приведение переменной массива в указатель теряет эту информацию.

Ответ 3

Я хочу преобразовать 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-массив не является массивом указателей, это массив массивов.

Ответ 4

Это ! Все возможно! Но это , чтобы он требовал некоторого уровня понимания.

Для этого давайте начнем с простого примера из 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;
}

Live Example

Обратите внимание, что в этом примере, хотя я ссылаюсь на 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

Ответ 5

Вы не можете. Они принципиально разные типы.

Ответ 6

Не уверен, что вы искали что-то вроде этого. Вы должны предоставить более подробную информацию о том, чего вы хотите достичь. Это принципиально разные типы. Одно из решений - ниже.

Для записи, если кто-то сочтет это полезным:

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