En un proyecto, alguien empujó esta línea:
double (*e)[n+1] = malloc((n+1) * sizeof(*e));
Lo que supuestamente crea una matriz bidimensional de (n + 1) * (n + 1) dobles.
Supuestamente , digo, porque hasta ahora, nadie a quien le pregunté podría decirme qué hace esto, exactamente, ni de dónde se originó o por qué debería funcionar (lo que supuestamente sí funciona, pero aún no lo estoy comprando).
Quizás me estoy perdiendo algo obvio, pero agradecería que alguien me lo explicara arriba. Porque personalmente, me sentiría mucho mejor si usáramos algo que realmente comprendamos.
c
arrays
multidimensional-array
malloc
allocation
Usuario1291
fuente
fuente

n+1pero en cambiodouble (*e)[rows] = malloc(columns * sizeof *e);Respuestas:
La variable
ees un puntero a una matriz den + 1elementos de tipodouble.El uso del operador de desreferencia en
ele da el tipo base delecual es "matriz den + 1elementos de tipodouble".La
mallocllamada simplemente toma el tipo base dee(explicado anteriormente) y obtiene su tamaño, lo multiplica porn + 1y pasa ese tamaño a lamallocfunción. Esencialmente, asigna una matriz den + 1matrices den + 1elementos dedouble.fuente
sizeof(*e)es equivalente asizeof(double [n + 1]). Multiplique eso conn + 1y obtendrá suficiente.n+1ambas dimensiones, el resultado será cuadrado. Si lo hacedouble (*e)[cols] = malloc(rows * sizeof(*e));, el resultado tendrá el número de filas y columnas que haya especificado.int rows = n+1yint cols = n+1. Dios nos salve del código inteligente.Esta es la forma típica en que debe asignar matrices 2D de forma dinámica.
ees un puntero de matriz a una matriz de tipodouble [n+1].sizeof(*e)por lo tanto, da el tipo del tipo apuntado, que es el tamaño de unadouble [n+1]matriz.n+1tales matrices.epara que apunte a la primera matriz de esta matriz de matrices.ease[i][j]para acceder a elementos individuales en la matriz 2D.Personalmente creo que este estilo es mucho más fácil de leer:
fuente
ptr = malloc(sizeof *ptr * count)estilo.malloc(row*col*sizeof(double))ocurre cuando serow*col*sizeof()desborda, pero nosizeof()*row*coles así. (por ejemplo, fila, col sonint)sizeof *e * (n+1)es más fácil de mantener; si alguna vez decide cambiar el tipo base (dedoublealong double, por ejemplo), entonces solo necesita cambiar la declaración dee; no es necesario modificar lasizeofexpresión en lamallocllamada (lo que ahorra tiempo y lo protege de cambiarla en un lugar pero no en el otro).sizeof *esiempre te dará el tamaño correcto.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:Simple, ¿verdad? La expresión
*ptiene tipoT, por lo quesizeof *pda el mismo resultado quesizeof (T), por lo que estamos asignando suficiente espacio para unaNmatriz de elementos deT. Esto es cierto para cualquier tipoT.Ahora, sustituyamos
Tpor un tipo de matriz comoR [10]. Entonces nuestra asignación se convierte enLa 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 deT *, es ahoraR (*)[10]. La expresión*ptiene tipoTque es tipoR [10], por lo quesizeof *pes equivalente a losizeof (T)que es equivalente asizeof (R [10]). Así que estamos asignando suficiente espacio para una matrizNpor10elemento deR.Podemos llevar esto aún más lejos si queremos; supongamos que
Res en sí mismo un tipo de matrizint [5]. Sustituye esoRy obtenemosLo mismo pasa -
sizeof *pes la misma quesizeof (int [10][5]), y que terminan asignando un trozo contiguo de memoria lo suficientemente grande como para contener unaNpor10por5variedad deint.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. Sipes un puntero aT, puede acceder al valor apuntado ya sea desreferenciando explícitamente con el*operador unario :o usando el
[]operador de subíndice: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 punterop:Ahora, hagamos nuestra operación de sustitución nuevamente y reemplacemos
Tcon el tipo de matrizR [10]:Una diferencia inmediatamente aparente; estamos desreferenciando explícitamente
pantes de aplicar el operador de subíndice. No queremos subíndices enp, queremos subíndices en lo quepapunta (en este caso, la matrizarr[0]). Dado que unario*tiene menor precedencia que el[]operador de subíndice , tenemos que usar paréntesis para agrupar explícitamentepcon*. Pero recuerde que desde arriba*pes lo mismo quep[0], por lo que podemos sustituirlo poro solo
Por lo tanto, si
papunta a una matriz 2D, podemos indexar en esa matriz de la siguientepmanera:Llevando esto a la misma conclusión anterior y sustituyéndolo
Rporint [5]:Esto funciona igual si
papunta a una matriz regular o si apunta a la memoria asignadamalloc.Este modismo tiene los siguientes beneficios:
free. Nuevamente, no es cierto con el método de asignación por partes, donde debe desasignar cada unoarr[i]antes de poder desasignararr.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.
fuente