2010-04-02 18 views
126

En C, sé que puedo asignar dinámicamente una matriz de dos dimensiones en el montón usando el siguiente código:¿Cómo se formatean matrices multidimensionales en la memoria?

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

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

Claramente, esto en realidad crea una matriz unidimensional de punteros a un manojo de uno separado dimensionales de números enteros, y "el Sistema" se puede averiguar a qué me refiero cuando pido:

someNumbers[4][2]; 

Pero cuando declaro estáticamente una matriz 2D, como en la siguiente línea ...:

int someNumbers[ARRAY_ROWS][ARRAY_COLUMNS]; 

... ¿se crea una estructura similar en la pila, o es de otra forma completamente? (es decir, ¿es un conjunto de punteros 1D? En caso negativo, ¿qué es y cómo se resuelven las referencias a él?)

También, cuando dije "El sistema", ¿qué es realmente responsable de calcular eso? ¿fuera? El kernel? ¿O el compilador de C lo resuelve durante la compilación?

+5

Daría más de +1 si pudiera. –

+0

** Advertencia **: ¡No hay una matriz 2D en este código! – Olaf

Respuesta

110

Una matriz bidimensional estática se parece a una matriz de matrices, simplemente se presenta de forma contigua en la memoria. Las matrices no son lo mismo que los indicadores, pero debido a que a menudo puede usarlas de manera bastante intercambiable, a veces puede ser confuso. Sin embargo, el compilador realiza un seguimiento adecuado, lo que hace que todo se alinee bien. Tienes que tener cuidado con las matrices 2D estáticas como mencionas, ya que si tratas de pasar una a una función tomando un parámetro int **, las cosas malas van a suceder. Aquí está un ejemplo rápido:

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

En la memoria se parece a esto:

0 1 2 3 4 5 

exactamente lo mismo que:

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

Pero si se intenta pasar array1 a esta función:

void function1(int **a); 

obtendrá una advertencia (y la aplicación dejará de acceder a la matriz correctamente):

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

Debido a que una matriz 2D no es lo mismo que int **. La descomposición automática de una matriz en un puntero solo tiene "un nivel de profundidad", por así decirlo. Es necesario para declarar la función como:

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

o

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

para que todo sea feliz.

Este mismo concepto se extiende a n matrices tridimensionales. Sin embargo, tomar ventaja de este tipo de negocios divertidos en su aplicación generalmente hace que sea más difícil de entender. Así que ten cuidado.

+0

Gracias por la explicación. Así que "void function2 (int a [] [2]);" aceptará los 2D declarados estática y dinámicamente? Y supongo que sigue siendo una buena práctica/esencial pasar la longitud del conjunto si la primera dimensión se deja como []? –

+1

@Chris No lo creo, le resultará difícil hacer que C swizzle sea una pila o una matriz asignada globalmente en un conjunto de punteros. –

+0

Ciertamente un [] [] debería funcionar!En C, cada matriz es solo un puntero con un desplazamiento, la asignación de memoria es la única diferencia real. No esperaría que la advertencia de ** mencionada anteriormente impida la compilación, aunque eso dependería del compilador y la configuración. –

5

En respuesta a su también: Ambos, aunque el compilador está haciendo la mayor parte del trabajo pesado.

En el caso de matrices asignadas estáticamente, "The System" será el compilador. Se reservará la memoria como lo haría para cualquier variable de pila.

En el caso de la matriz malloc'd, "The System" será el implementador de malloc (el kernel generalmente). Todo lo que el compilador asignará es el puntero base.

El compilador siempre manejará el tipo como lo que se declara que sea, excepto en el ejemplo que dio Carl donde se puede deducir el uso intercambiable. Esta es la razón por la cual si pasa un [] [] a una función, debe suponer que se trata de un plano estáticamente asignado, donde ** se supone que es un puntero a un puntero.

+0

@Jon L. I no diría que malloc es implementado por el kernel, pero por la libc encima de las primitivas del kernel (como brk) –

+0

@ManuelSelva: dónde y cómo se implementa 'malloc' no está especificado por el estándar y se deja a la implementación, resp Para entornos independientes, es opcional, como todas las partes de la biblioteca estándar que requieren funciones de enlace (de eso se derivan los requisitos, no literalmente, lo que dice el estándar). Para algunos entornos alojados modernos, de hecho depende de las funciones del núcleo, ya sea el material completo, o (p. ej., Linux), como usted escribió usando stdlib y primitivas del kernel. Para sistemas de proceso único de memoria no virtual, puede ser stdlib solamente. – Olaf

67

La respuesta se basa en la idea de que C realmente no tiene tiene matrices 2D - tiene matrices de matrices. Cuando se declara lo siguiente:

int someNumbers[4][2]; 

Usted está solicitando someNumbers a ser una matriz de 4 elementos, donde cada elemento de esa matriz es de tipo int [2] (que es en sí mismo una serie de 2 int s).

La otra parte del rompecabezas es que las matrices siempre se disponen contiguamente en la memoria. Si usted pide:

sometype_t array[4]; 

entonces que siempre se verá como esto:

| sometype_t | sometype_t | sometype_t | sometype_t | 

(4 sometype_t objetos dispuestas una junto a la otra, sin espacios intermedios). Por lo que en sus someNumbers gama-de-matrices, que va a tener este aspecto:

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

Y cada elemento int [2] es en sí misma una matriz, que se ve así:

| int  | int  | 

por lo general, se obtiene esto:

| int | int | int | int | int | int | int | int | 
+0

mirando el diseño final me hace pensar que int a [ ] [] se puede acceder como int * ... r Ocho? –

+1

@ user3238855: Los tipos no son compatibles, pero si obtienes un puntero al primer 'int' en la matriz de matrices (por ejemplo, evaluando' a [0] 'o' & a [0] [0] '), entonces sí, puede compensar eso para acceder secuencialmente a cada 'int'). – caf

22
unsigned char MultiArray[5][2]={{0,1},{2,3},{4,5},{6,7},{8,9}}; 

en la memoria es igual a:

unsigned char SingleArray[10]={0,1,2,3,4,5,6,7,8,9}; 
1

Para acceder a una matriz 2D particular, considere el mapa de memoria para una declaración de matriz como se muestra en el código de abajo:

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

para acceder a cada elemento, su suficiente para simplemente pasar a la cual matriz que está interesado en lo parámetros a la función. A continuación, use desplazamiento para columna para acceder a cada elemento individualmente.

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