Как форматируются многомерные массивы в памяти?
В 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 массив указателей? Если нет, то что это, и как ссылки на него выясняются?)
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 на самом деле есть 2Д массивы - это массивы-это массивы. Когда вы объявляете это:
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
для доступа к каждому элементу достаточно просто передать интересующий вас массив в качестве параметров функции. Затем используйте offset for column для доступа к каждому элементу по отдельности.
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;
}