2010-04-02 2 views
126

В 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

Я бы дал больше, чем +1, если мог. –

+0

** Предупреждение **: в этом коде нет 2D-массива! – Olaf

ответ

110

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

+0

Спасибо за объяснение. Итак, "void function2 (int a [] [2]);" будет принимать как статически, так и динамически объявленные 2D? И я полагаю, что все еще хорошая практика/существенная передача в длину массива, если первое измерение остается как []? –

+1

@Chris Я так не думаю - вам будет сложно сделать C swizzle в стеке или глобально выделенном массиве в кучу указателей. –

+0

Конечно, [] [] должен работать!В C каждый массив - это всего лишь указатель со смещением, распределение памяти - единственное реальное различие. Я бы не ожидал предупреждения для упомянутого выше **, чтобы предотвратить компиляцию, хотя это будет зависеть от компилятора и настроек. –

5

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

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

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

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

+0

@Jon L. I не сказал бы, что malloc реализуется ядром, но libc поверх примитивов ядра (например, brk) –

+0

@ManuelSelva: где и как реализован «malloc», не указан стандартом и оставлен для реализации, соответственно . Для автономных сред это необязательно, как и все части стандартной библиотеки, требующие связывания функций (это то, что на самом деле приводит к требованиям, а не буквально, что означает стандартные состояния). Для некоторых современных размещенных средах он действительно полагается на функции ядра, либо полный материал или (например, Linux), как вы писали, используя как stdlib, так и примитивы ядра. Для систем с одним процессом без виртуальной памяти это может быть только stdlib. – Olaf

67

Ответ основан на идее, что 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 | 
+0

, глядя на окончательный макет, заставляет меня думать, что int a [ ] [] можно получить как int * ... r IGHT? –

+1

@ user3238855: Типы несовместимы, но если вы получаете указатель на первый 'int' в массиве массивов (например, путем вычисления' a [0] 'или' & a [0] [0] '), тогда да, вы можете компенсировать это для последовательного доступа к каждому 'int'). – caf

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

Чтобы получить доступ к конкретной 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; 
} 
Смежные вопросы