Как многомерные массивы отформатированы в памяти?
В 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 сортирует его при компиляции?
Ответы
Ответ 1
Статический двухмерный массив выглядит как массив массивов - он просто выложен в памяти. Массивы - это не то же самое, что и указатели, но потому, что вы часто можете использовать их в значительной степени взаимозаменяемо, иногда это может запутать. Компилятор правильно отслеживает, что делает все хорошо. Вы должны быть осторожны со статическими 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-мерные массивы. Однако использование такого смешного бизнеса в вашем приложении, как правило, затрудняет его понимание. Поэтому будьте осторожны.
Ответ 2
Ответ основан на идее, что 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 |
Ответ 3
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};
Ответ 4
В ответ на ваш вопрос: оба, хотя компилятор делает большую часть тяжелой работы.
В случае статически распределенных массивов, "Система" будет компилятором. Он сохранит память, как это было бы для любой переменной стека.
В случае массива malloc'd "Система" будет исполнятелем malloc (обычно ядро). Весь компилятор будет выделять базовый указатель.
Компилятор всегда будет обрабатывать тип как то, что они объявлены, кроме как в примере, который Карл дал, где он может найти взаимозаменяемое использование. Вот почему, если вы передаете функцию [] [] в функцию, она должна предположить, что это статически распределенная квартира, где ** считается указателем на указатель.
Ответ 5
Чтобы получить доступ к определенному 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;
}