Как форматируются многомерные массивы в памяти?


В 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];

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

кроме того, когда я сказал: "система", что на самом деле отвечает за выяснение этого? Ядро? Или компилятор C сортирует его во время компиляции?

5 138

5 ответов:

статический двумерный массив выглядит как массив массивов-он просто выложен последовательно в памяти. Массивы-это не то же самое, что указатели, но поскольку вы часто можете использовать их в значительной степени взаимозаменяемо, это может иногда сбивать с толку. Компилятор отслеживает правильно, хотя, что делает все выстраиваются красиво. Вы должны быть осторожны со статическими 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

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

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

или

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

все счастливы.

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

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

int someNumbers[4][2];

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

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

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  |
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};

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

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

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

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

для доступа к определенному 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;
}