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

Как хранятся 3D-массивы в C?

Я понимаю, что массивы в C выделены в строчном порядке. Поэтому для массива 2 x 3:

0  1
2  3
4  5

Сохраняется в памяти как

0 1 2 3 4 5

Однако, если у меня есть массив размером 2 x 3 x 2:

0  1
2  3
4  5

и

6  7
8  9
10 11

Как они хранятся в памяти? Как раз подряд:

0 1 2 3 4 5 6 7 8 9 10 11

Или это какой-то другой способ? Или это зависит от чего-то?

4b9b3361

Ответ 1

Все "размеры" сохраняются последовательно в памяти.

Рассмотрим

    int arr[4][100][20];

вы можете сказать, что arr[1] и arr[2] (типа int[100][20]) смежны или что arr[1][42] и arr[1][43] (типа int[20]) смежны или что arr[1][42][7] и arr[1][42][8] (типа int) смежны

Ответ 2

На низком уровне нет такой вещи, как многомерный массив. Существует только плоский блок памяти, достаточно большой, чтобы удерживать определенное количество элементов. В C многомерный массив концептуально представляет собой массив, элементы которого также являются массивами. Итак, если вы это сделаете:

int array[2][3];

Концептуально вы получите:

array[0] => [0, 1, 2]
array[1] => [0, 1, 2]

Это приводит к тому, что элементы упорядочиваются в памяти, потому что array[0] и array[1] на самом деле не содержат никаких данных, они являются лишь ссылками на два внутренних массива. Обратите внимание, что это означает, что только записи [0, 1, 2] фактически занимают пространство в памяти. Если вы расширите этот шаблон до следующего измерения, вы увидите, что:

int array[2][3][2];

... даст вам структуру вроде:

array[0] => [0] => [0, 1]
            [1] => [0, 1]
            [2] => [0, 1]
array[1] => [0] => [0, 1]
            [1] => [0, 1]
            [2] => [0, 1]

Который продолжает упорядочивать элементы последовательно в памяти (как указано выше, только записи [0, 1] фактически занимают пространство в памяти, все остальное является лишь частью ссылки на одну из этих записей). Как вы можете видеть, этот шаблон будет продолжаться независимо от того, сколько у вас размеров.

И просто для удовольствия:

int array[2][3][2][5];

Дает вам:

array[0] => [0] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
            [1] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
            [2] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
array[1] => [0] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
            [1] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
            [2] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]

Ответ 3

Да, вы правы - они хранятся последовательно. Рассмотрим этот пример:

#include <stdio.h>

int array3d[2][3][2] = {
  {{0, 1}, {2, 3}, {3, 4}},
  {{5, 6}, {7, 8}, {9, 10}}
};

int main()
{
  int i;
  for(i = 0; i < 12; i++) {
    printf("%d ", *((int*)array3d + i));
  }
  printf("\n");
  return 0;
}

Выход:

0 1 2 3 3 4 5 6 7 8 9 10

Ответ 4

Да, они просто хранятся в последовательном порядке. Вы можете проверить это следующим образом:

#include <stdio.h>

int main (int argc, char const *argv[])
{
  int numbers [2][3][4] = {{{1,2,3,4},{5,6,7,8},{9,10,11,12}}
                          ,{{13,14,15,16},{17,18,19,20},{21,22,23,24}}};

  int i,j,k;

  printf("3D:\n");
  for(i=0;i<2;++i)
    for(j=0;j<3;++j)
      for(k=0;k<4;++k)
        printf("%i ", numbers[i][j][k]);

  printf("\n\n1D:\n");
  for(i=0;i<24;++i)
    printf("%i ", *((int*)numbers+i));

  printf("\n");

  return 0;
}

Это означает, что обращения к многоиндексному массиву с размерами (N, M, L) преобразуются в одномерные обращения, подобные этому:

array[i][j][k] = array[M*L*i + L*j + k]

Ответ 5

Я думаю, вы ответили на свой вопрос. Многомерные массивы хранятся в строчном порядке.

См. раздел 3.3.2.1 спецификации ANSI C (также есть конкретный пример):

Операторы с последующим индексом назначают члена многомерный объект массива. Если E - n-мерный массив (n = 2) с размерами я x j "x... x" k, тогда E (используется как не lvalue) преобразуется в указатель на (n -1) -мерный массив с размеры j "x... x" k. Если к этому применен унарный оператор * указатель явно или неявно в результате подписи, результатом является направленный (n -1) -мерный массив, который сам по себе является преобразуется в указатель, если используется иначе, чем lvalue. Следует из этого, что массивы хранятся в строчном порядке (последний индекс изменяется быстрее).

В вашем примере вы можете просто попробовать и посмотреть - http://codepad.org/10ylsgPj

Ответ 6

Скажем, у вас есть массив char arr[3][4][5]. Это массив из 3 массивов из 4 массивов из 5 символов.

Для простоты скажем, что значение в arr[x][y][z] равно xyz, а в arr[1][2][3] мы сохраняем 123.

Итак, макет в памяти:

  |  00  01  02  03  04  05  06  07  08  09  10  11  12  13  14  15  16  17  18  19
--+--------------------------------------------------------------------------------   
00| 000 001 002 003 004 010 011 012 013 014 020 021 022 023 024 030 031 032 033 034 
20| 100 101 102 103 104 110 111 112 113 114 120 121 122 123 124 130 131 132 133 134 
40| 200 201 202 203 204 210 211 212 213 214 220 221 222 223 224 230 231 232 233 234

arr[0], arr[1] и arr[2] идут один за другим, но каждый элемент в нем имеет тип char[4][5] (это три строки в таблице).

arr[x][0] - arr[x][3] также идут один за другим, и каждый элемент в них имеет тип char[5] (это четыре части каждой строки в таблице, 000 - 004 - один элемент arr[0][0])

arr[x][y][0] - arr[x][y][4] - это 5 байтов, которые идут один за другим.

Ответ 7

Чтобы ответить на комментарий к главному вопросу (он будет несколько длинным, поэтому я решил пойти с ответом, а не комментарием):

Если массивы в C объявлены как array[ny][nx], где ny и nx - количество элементов в направлениях y и x. Кроме того, означает ли это, что мой 3D-массив должен быть объявлен как array[nz][ny][nx]?

В математике матрица MxN имеет M строк и N столбцов. Обычным обозначением для элемента матрицы является a(i,j), 1<=i<=M, 1<=j<=N. Итак, первая матрица в вашем вопросе - матрица 3x2.

Действительно, он отличается от обозначения, обычно используемого, например. Элементы GUI. Растровая карта 800x600 имеет 800 пикселей по горизонтали (вдоль оси X) и 600 пикселей по вертикали (вдоль оси Y). Если кто-то захочет описать его как матрицу, то в математической записи это будет матрица размером 600x800 (600 строк, 800 столбцов).

Теперь многомерные массивы в C хранятся в памяти таким образом, что a[i][j+1] находится рядом с a[i][j], а a[i+1][j] - N элементов. Обычно говорят, что "последний индекс изменяется быстрее всего" или часто "хранится по строкам": строка (то есть элементы с одним и тем же первым индексом) в двумерной матрице смежно помещается в памяти, а столбец (тот же самый второй индекс ) состоят из элементов, лежащих далеко друг от друга. Важно знать соображения производительности: доступ к соседним элементам обычно намного быстрее (из-за кэшей HW и т.д.), Поэтому, например, вложенные циклы должны быть организованы таким образом, чтобы самый внутренний выполнялся по последнему индексу.

Вернемся к вопросу: если ваша мысленная картина (абстракция) двумерного массива - это решетка в картезианских координатах, то да, вы можете думать об этом как array[ny][nx] в C. Однако, если вам нужно описать реальную 2D или 3D-данные в виде массива, выбор индексов, вероятно, зависит от других факторов: форматов данных, удобной нотации, производительности и т.д. Например, если представление в памяти для растрового изображения array[NX][NY] в формате, которое вам нужно работайте с, вы объявите это таким образом, и, возможно, вам даже не нужно знать, что растровое изображение становится своего рода "транспонированным":)

Ответ 8

3D-массив - это расширенный массив 2d.

Например, у нас есть массив - int arr (3) (5) (6);

Это массив, который состоит из двух массивов 2d, где массив будет иметь 2d-массив, имеющий 4 строки и 3 столбца.