¿Cómo se almacenan las matrices 3D en C?

7 minutos de lectura

Entiendo que las matrices en C se asignan en orden de fila principal. Por lo tanto, para una matriz de 2 x 3:

0  1
2  3
4  5

Se almacena en la memoria como

0 1 2 3 4 5

Sin embargo, ¿qué sucede si tengo una matriz de 2 x 3 x 2?

0  1
2  3
4  5

y

6  7
8  9
10 11

¿Cómo se almacenan en la memoria? Es simplemente consecutivo como:

0 1 2 3 4 5 6 7 8 9 10 11

¿O es de otra manera? O depende de algo?

  • La primera matriz en la pregunta es en realidad 3×2, por ejemplo int a[3][2];

    – Alexei Kukanov

    7 mayo 2011 a las 12:44


  • @Alexey: Eso es interesante, tal vez esa sea la raíz de mi confusión. ¿Deberían declararse las matrices en C como array[ny][nx] donde ny y nx son el número de elementos en la dirección y y x. Además, ¿eso significa que mi matriz 3D debe declararse como array[nz][ny][nx]?

    – robintw

    7 mayo 2011 a las 19:17

avatar de usuario
aroth

En un nivel bajo, no existe tal cosa como una matriz multidimensional. Solo hay un bloque plano de memoria, lo suficientemente grande como para contener una cantidad determinada de elementos. En C, una matriz multidimensional es conceptualmente una matriz cuyos elementos también son matrices. Así que si lo haces:

int array[2][3];

Conceptualmente terminas con:

array[0] => [0, 1, 2]
array[1] => [0, 1, 2]

Esto da como resultado que los elementos se ordenen de forma contigua en la memoria, porque array[0] y array[1] en realidad no contienen ningún dato, son solo referencias a las dos matrices internas. Tenga en cuenta que esto significa que sólo el [0, 1, 2] las entradas realmente ocupan espacio en la memoria. Si extiende este patrón a la siguiente dimensión, puede ver que:

int array[2][3][2];

… le dará una estructura como:

array[0] => [0] => [0, 1]
            [1] => [0, 1]
            [2] => [0, 1]
array[1] => [0] => [0, 1]
            [1] => [0, 1]
            [2] => [0, 1]

Que continúa ordenando los elementos consecutivamente en la memoria (como arriba, solo los [0, 1] las entradas en realidad ocupan espacio en la memoria, todo lo demás es solo parte de una referencia a una de estas entradas). Como puede ver, este patrón continuará sin importar cuántas dimensiones tenga.

Y solo por diversión:

int array[2][3][2][5];

Te dio:

array[0] => [0] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
            [1] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
            [2] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
array[1] => [0] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
            [1] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
            [2] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]

Todas las “dimensiones” se almacenan consecutivamente en la memoria.

Considerar

    int arr[4][100][20];

puedes decir eso arr[1] y arr[2] (de tipo int[100][20]) son contiguos
o eso arr[1][42] y arr[1][43] (de tipo int[20]) son contiguos
o eso arr[1][42][7] y arr[1][42][8] (de tipo int) son contiguos

avatar de usuario
sje397

Sí, tienes razón, se almacenan consecutivamente. Considere este ejemplo:

#include <stdio.h>

int array3d[2][3][2] = {
  {{0, 1}, {2, 3}, {3, 4}},
  {{5, 6}, {7, 8}, {9, 10}}
};

int main()
{
  int i;
  for(i = 0; i < 12; i++) {
    printf("%d ", *((int*)array3d + i));
  }
  printf("\n");
  return 0;
}

Producción:

0 1 2 3 3 4 5 6 7 8 9 10

  • Hola, solo tengo curiosidad acerca de cómo puedo visualizar esta matriz 3D como una matriz y cómo ha asignado los elementos.

    – bhansa

    24 de abril de 2016 a las 9:04

avatar de usuario
Thies Heidecke

Sí, solo se almacenan en orden consecutivo. Puedes probar eso así:

#include <stdio.h>

int main (int argc, char const *argv[])
{
  int numbers [2][3][4] = {{{1,2,3,4},{5,6,7,8},{9,10,11,12}}
                          ,{{13,14,15,16},{17,18,19,20},{21,22,23,24}}};

  int i,j,k;

  printf("3D:\n");
  for(i=0;i<2;++i)
    for(j=0;j<3;++j)
      for(k=0;k<4;++k)
        printf("%i ", numbers[i][j][k]);

  printf("\n\n1D:\n");
  for(i=0;i<24;++i)
    printf("%i ", *((int*)numbers+i));

  printf("\n");

  return 0;
}

Eso significa que los accesos a un arreglo multiindexado con dimensiones (N,M,L) se transforman en accesos unidimensionales como este:

array[i][j][k] = array[M*L*i + L*j + k]

Creo que has respondido tu propia pregunta. Las matrices multidimensionales se almacenan en orden de fila principal.

Consulte la sección 3.3.2.1 de la especificación ANSI C (también hay un ejemplo específico):

Los operadores de subíndices sucesivos designan un miembro de un objeto de matriz multidimensional. Si E es una matriz de n dimensiones ( n = 2) con dimensiones ixj “x … x” k , entonces E (que no se usa como un valor l) se convierte en un puntero a una matriz de ( n -1) dimensiones con dimensiones j “x … x” k . Si el operador unario * se aplica a este puntero explícitamente, o implícitamente como resultado de subíndices, el resultado es la matriz dimensional apuntada ( n -1), que a su vez se convierte en un puntero si se usa como algo distinto de un valor l . De esto se deduce que las matrices se almacenan en orden de fila principal (el último subíndice varía más rápido).

Para su ejemplo, puede probarlo y ver: http://codepad.org/10ylsgPj

Digamos que tienes una matriz char arr[3][4][5]. Es una matriz de 3 matrices de 4 matrices de 5 caracteres.

Para simplificar, digamos que el valor en arr[x][y][z] es xyz y en arr[1][2][3] nosotros almacenamos 123.

Así que el diseño en la memoria es:

  |  00  01  02  03  04  05  06  07  08  09  10  11  12  13  14  15  16  17  18  19
--+--------------------------------------------------------------------------------   
00| 000 001 002 003 004 010 011 012 013 014 020 021 022 023 024 030 031 032 033 034 
20| 100 101 102 103 104 110 111 112 113 114 120 121 122 123 124 130 131 132 133 134 
40| 200 201 202 203 204 210 211 212 213 214 220 221 222 223 224 230 231 232 233 234

arr[0], arr[1] y arr[2] vienen uno tras otro, pero cada elemento en el es del tipo char[4][5] (esas son las tres filas de la tabla).

arr[x][0] - arr[x][3] también vienen uno tras otro, y cada elemento en ellos es del tipo char[5] (esas son las cuatro partes de cada línea en la tabla, 000 – 004 es un elemento de arr[0][0] )

arr[x][y][0] - arr[x][y][4] son 5 bytes que van viniendo uno tras otro.

avatar de usuario
Aleksey Kukanov

Para responder el comentario de OP a la pregunta principal (será algo largo, así que decidí ir con una respuesta, no con un comentario):

¿Deberían declararse las matrices en C como array[ny][nx] donde ny y nx son el número de elementos en la dirección y y x. Además, ¿eso significa que mi matriz 3D debe declararse como array[nz][ny][nx]?

En matemáticas, una matriz MxN tiene M filas y N columnas. Una notación habitual para un elemento de matriz es a(i,j), 1<=i<=M, 1<=j<=N. Entonces, la primera matriz en su pregunta es una matriz de 3×2.

De hecho, es diferente de la notación que se suele utilizar, por ejemplo, para los elementos de la GUI. Un mapa de bits de 800×600 tiene 800 píxeles horizontalmente (a lo largo del eje X) y 600 píxeles verticalmente (a lo largo del eje Y). Si alguien quisiera describirlo como una matriz, en notación matemática sería una matriz de 600×800 (600 filas, 800 columnas).

Ahora, las matrices multidimensionales en C se almacenan en la memoria de tal manera que a[i][j+1] está cerca de a[i][j] tiempo a[i+1][j] está a N elementos de distancia. Se suele decir que “el último subíndice varía más rápido”, o a menudo como “almacenado por filas”: una fila (es decir, elementos con el mismo primer índice) en una matriz bidimensional se ha colocado contiguamente en la memoria mientras que una columna (mismo segundo índice ) consisten en elementos que se encuentran lejos unos de otros. Es importante tener en cuenta las consideraciones de rendimiento: el acceso a los elementos vecinos suele ser mucho más rápido (debido a las memorias caché HW, etc.), por lo que, por ejemplo, los bucles anidados deben organizarse de manera que el más interno itere sobre el último índice.

Volviendo a la pregunta: si su imagen mental (abstracción) de una matriz 2D es la de una red en coordenadas cartesianas, entonces sí, puede pensar en ella como array[NY][NX] en C. Sin embargo, si necesita describir datos 2D o 3D reales como una matriz, la elección de índices probablemente depende de otras cosas: formatos de datos, notación conveniente, rendimiento, etc. Por ejemplo, si la representación en memoria para un mapa de bits es array[NX][NY] en un formato con el que necesita trabajar, lo declarará de esa manera, y tal vez ni siquiera necesite saber que el mapa de bits se “transpone” 🙂

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad