Как многомерные массивы форматируются в памяти? - PullRequest
154 голосов
/ 02 апреля 2010

В C я знаю, что могу динамически распределить двумерный массив в куче, используя следующий код:

int** someNumbers = malloc(arrayRows*sizeof(int*));

for (i = 0; i < arrayRows; i++) {
    someNumbers[i] = malloc(arrayColumns*sizeof(int));
}

Очевидно, что на самом деле это создает одномерный массив указателей на группу отдельных одномерных массивов целых чисел, и «Система» может понять, что я имею в виду, когда спрашиваю:

someNumbers[4][2];

Но когда я статически объявляю 2D-массив, как в следующей строке ...:

int someNumbers[ARRAY_ROWS][ARRAY_COLUMNS];

... создается ли подобная структура в стеке или она полностью другой формы? (т.е. это массив одномерных указателей? Если нет, то что это такое и как выясняются ссылки на него?)

Кроме того, когда я сказал: «Система», что на самом деле отвечает за это? Ядро? Или компилятор C разбирает его во время компиляции?

Ответы [ 5 ]

124 голосов
/ 02 апреля 2010

Статический двумерный массив выглядит как массив массивов - он просто расположен непрерывно в памяти. Массивы - это не то же самое, что указатели, но поскольку вы часто можете использовать их почти взаимозаменяемо, иногда это может сбить с толку. Тем не менее, компилятор правильно отслеживает, что делает все правильно. Вы должны быть осторожны со статическими 2D-массивами, как вы упомянули, поскольку, если вы попытаетесь передать один из них в функцию, принимающую параметр int **, могут произойти плохие вещи. Вот быстрый пример:

int array1[3][2] = {{0, 1}, {2, 3}, {4, 5}};

В памяти выглядит так:

0 1 2 3 4 5

точно так же, как:

int array2[6] = { 0, 1, 2, 3, 4, 5 };

Но если вы попытаетесь передать array1 этой функции:

void function1(int **a);

вы получите предупреждение (и приложению не удастся правильно получить доступ к массиву):

warning: passing argument 1 of ‘function1’ from incompatible pointer type

Поскольку двумерный массив отличается от int **. Автоматическое разложение массива на указатель идет, так сказать, только на один уровень. Вам нужно объявить функцию как:

void function2(int a[][2]);

или

void function2(int a[3][2]);

Чтобы сделать все счастливым.

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

75 голосов
/ 02 апреля 2010

Ответ основан на идее, что C на самом деле не имеет 2D-массивов - у него есть массивы-массивы. Когда вы заявляете это:

int someNumbers[4][2];

Вы просите, чтобы someNumbers был массивом из 4 элементов, где каждый элемент этого массива имеет тип int [2] (который сам по себе является массивом 2 int с).

Другая часть головоломки состоит в том, что массивы всегда располагаются непрерывно в памяти. Если вы спросите:

sometype_t array[4];

тогда это всегда будет выглядеть так:

| sometype_t | sometype_t | sometype_t | sometype_t |

(4 sometype_t объекты, расположенные рядом друг с другом, без пробелов между ними). Итак, в вашем массиве массивов someNumbers это будет выглядеть так:

| int [2]    | int [2]    | int [2]    | int [2]    |

И каждый элемент int [2] представляет собой массив, который выглядит следующим образом:

| int        | int        |

Итак, в целом вы получите это:

| int | int  | int | int  | int | int  | int | int  |
26 голосов
/ 01 февраля 2013
unsigned char MultiArray[5][2]={{0,1},{2,3},{4,5},{6,7},{8,9}};

в памяти равно:

unsigned char SingleArray[10]={0,1,2,3,4,5,6,7,8,9};
5 голосов
/ 02 апреля 2010

В ответ на ваш вопрос: оба, хотя компилятор выполняет большую часть тяжелой работы.

В случае статически распределенных массивов, «Система» будет компилятором. Он зарезервирует память, как для любой переменной стека.

В случае массива malloc, "Система" будет исполнителем malloc (обычно ядра). Все, что выделит компилятор, это базовый указатель.

Компилятор всегда будет обрабатывать тип так, как он объявлен, за исключением приведенного Карлом примера, в котором он может определить взаимозаменяемое использование. Вот почему, если вы передаете [] [] функцию, она должна предполагать, что это статически распределенная квартира, где ** считается указателем на указатель.

1 голос
/ 09 февраля 2016

Для доступа к конкретному 2D-массиву рассмотрите карту памяти для объявления массива, как показано в коде ниже:

    0  1
a[0]0  1
a[1]2  3

Чтобы получить доступ к каждому элементу, достаточно просто передать интересующий вас массив в качестве параметров функции. Затем используйте смещение для столбца, чтобы получить доступ к каждому элементу индивидуально.

int a[2][2] ={{0,1},{2,3}};

void f1(int *ptr);

void f1(int *ptr)
{
    int a=0;
    int b=0;
    a=ptr[0];
    b=ptr[1];
    printf("%d\n",a);
    printf("%d\n",b);
}

int main()
{
   f1(a[0]);
   f1(a[1]);
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...