преобразование двумерного массива в указатель на указатель - PullRequest
17 голосов
/ 20 ноября 2011
Activity solution[a][b];

...

Activity **mother = solution;

Я хочу преобразовать двумерный массив объектов в указатель на указатель. Как я могу это сделать;

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

Ответы [ 6 ]

16 голосов
/ 20 ноября 2011

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

11 голосов
/ 20 ноября 2011

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

6 голосов
/ 20 ноября 2011

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

Youэто можно сделать и с двумерными массивами, но если у вас есть T[a][b], то он уменьшается до (T[b])*, потому что двумерный массив не является массивом указателей, это массив массивов.

5 голосов
/ 08 июля 2017

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

Для этого давайте начнем с простого примера 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 s занимают ровно 1 байт, поэтому адрес каждогопоследующие char в массиве будут просто следующими адресами.Это используется оператором Subscript следующим образом: 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

Наконец, давайте посмотрим, как хранит двумерные массивы.Учитывая: 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 указателя одномерного массива, но содержит содержимое 2 массивов.(Между прочим, на 32-разрядной машине, где не хранятся указатели, сохраняется 8 байтов памяти, что довольно существенно для 8-байтового 2-мерного массива.) Таким образом, попытка трактовать 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 для индексации, а именно:

  1. Какмногие массивы находятся в главном измерении: size(si2DArray)
  2. Сколько элементов в младшем измерении: size(*si2DArray)
  3. Какой размер в памяти младшего измерения: sizeof(*si2DArray)
  4. Каков тип элемента массива: sizeof(**si2DArray)

Таким образом, вы можете видеть, что потеря информации при преобразовании из массива в указатель является существенной.Вы можете испытать желание сохранить тип элемента, тем самым упрощая арифметику указателя.Стоит отметить, что только преобразование в char* считается определенным поведением reinterpret_cast: http://en.cppreference.com/w/cpp/language/reinterpret_cast#Type_aliasing

0 голосов
/ 25 июня 2015

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

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

// 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]);
    }
}
0 голосов
/ 20 ноября 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...