Este idioma cae naturalmente de la asignación de matrices 1D. Comencemos con la asignación de una matriz 1D de algún tipo arbitrario T:
T *p = malloc( sizeof *p * N );
Simple, ¿verdad? La expresión *p tiene tipo T, por lo que sizeof *pda el mismo resultado que sizeof (T), por lo que estamos asignando suficiente espacio para una Nmatriz de elementos de T. Esto es cierto para cualquier tipoT .
Ahora, sustituyamos Tpor un tipo de matriz como R [10]. Entonces nuestra asignación se convierte en
R (*p)[10] = malloc( sizeof *p * N);
La semántica aquí es exactamente la misma que la del método de asignación 1D; todo lo que ha cambiado es el tipo de p. En lugar de T *, es ahora R (*)[10]. La expresión *ptiene tipo Tque es tipo R [10], por lo que sizeof *pes equivalente a lo sizeof (T)que es equivalente a sizeof (R [10]). Así que estamos asignando suficiente espacio para una matriz Npor 10elemento de R.
Podemos llevar esto aún más lejos si queremos; supongamos que Res en sí mismo un tipo de matriz int [5]. Sustituye eso Ry obtenemos
int (*p)[10][5] = malloc( sizeof *p * N);
Lo mismo pasa - sizeof *pes la misma que sizeof (int [10][5]), y que terminan asignando un trozo contiguo de memoria lo suficientemente grande como para contener una Npor 10por 5variedad de int.
Así que ese es el lado de la asignación; ¿qué pasa con el lado de acceso?
Recuerde que la []operación de subíndice se define en términos de aritmética de punteros: a[i]se define como *(a + i)1 . Por tanto, el operador de subíndice desreferencia [] implícitamente un puntero. Si pes un puntero a T, puede acceder al valor apuntado ya sea desreferenciando explícitamente con el *operador unario :
T x = *p;
o usando el []operador de subíndice:
T x = p[0]; // identical to *p
Por lo tanto, si papunta al primer elemento de una matriz , puede acceder a cualquier elemento de esa matriz utilizando un subíndice en el puntero p:
T arr[N];
T *p = arr; // expression arr "decays" from type T [N] to T *
...
T x = p[i]; // access the i'th element of arr through pointer p
Ahora, hagamos nuestra operación de sustitución nuevamente y reemplacemos Tcon el tipo de matriz R [10]:
R arr[N][10];
R (*p)[10] = arr; // expression arr "decays" from type R [N][10] to R (*)[10]
...
R x = (*p)[i];
Una diferencia inmediatamente aparente; estamos desreferenciando explícitamente pantes de aplicar el operador de subíndice. No queremos subíndices en p, queremos subíndices en lo que p apunta (en este caso, la matriz arr[0] ). Dado que unario *tiene menor precedencia que el []operador de subíndice , tenemos que usar paréntesis para agrupar explícitamente pcon *. Pero recuerde que desde arriba *pes lo mismo que p[0], por lo que podemos sustituirlo por
R x = (p[0])[i];
o solo
R x = p[0][i];
Por lo tanto, si papunta a una matriz 2D, podemos indexar en esa matriz de la siguiente pmanera:
R x = p[i][j]; // access the i'th element of arr through pointer p;
// each arr[i] is a 10-element array of R
Llevando esto a la misma conclusión anterior y sustituyéndolo Rpor int [5]:
int arr[N][10][5];
int (*p)[10][5]; // expression arr "decays" from type int [N][5][10] to int (*)[10][5]
...
int x = p[i][j][k];
Esto funciona igual si papunta a una matriz regular o si apunta a la memoria asignada malloc.
Este modismo tiene los siguientes beneficios:
- Es simple: solo una línea de código, a diferencia del método de asignación por partes
T **arr = malloc( sizeof *arr * N );
if ( arr )
{
for ( size_t i = 0; i < N; i++ )
{
arr[i] = malloc( sizeof *arr[i] * M );
}
}
- Todas las filas de la matriz asignada son * contiguas *, lo que no es el caso con el método de asignación por partes anterior;
- Desasignar la matriz es igual de fácil con una sola llamada a
free. Nuevamente, no es cierto con el método de asignación por partes, donde debe desasignar cada uno arr[i]antes de poder desasignar arr.
A veces, es preferible el método de asignación por partes, como cuando su montón está muy fragmentado y no puede asignar su memoria como un fragmento contiguo, o desea asignar una matriz "irregular" donde cada fila puede tener una longitud diferente. Pero en general, esta es la mejor manera de hacerlo.
1. Recuerde que las matrices no son punteros; en cambio, las expresiones de matriz se convierten en expresiones de puntero según sea necesario.