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+1
pero en cambiodouble (*e)[rows] = malloc(columns * sizeof *e);
Respuestas:
La variable
e
es un puntero a una matriz den + 1
elementos de tipodouble
.El uso del operador de desreferencia en
e
le da el tipo base dele
cual es "matriz den + 1
elementos de tipodouble
".La
malloc
llamada simplemente toma el tipo base dee
(explicado anteriormente) y obtiene su tamaño, lo multiplica porn + 1
y pasa ese tamaño a lamalloc
función. Esencialmente, asigna una matriz den + 1
matrices den + 1
elementos dedouble
.fuente
sizeof(*e)
es equivalente asizeof(double [n + 1])
. Multiplique eso conn + 1
y obtendrá suficiente.n+1
ambas 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+1
yint 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.
e
es 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+1
tales matrices.e
para que apunte a la primera matriz de esta matriz de matrices.e
ase[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*col
es 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 (dedouble
along double
, por ejemplo), entonces solo necesita cambiar la declaración dee
; no es necesario modificar lasizeof
expresión en lamalloc
llamada (lo que ahorra tiempo y lo protege de cambiarla en un lugar pero no en el otro).sizeof *e
siempre 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
*p
tiene tipoT
, por lo quesizeof *p
da el mismo resultado quesizeof (T)
, por lo que estamos asignando suficiente espacio para unaN
matriz de elementos deT
. Esto es cierto para cualquier tipoT
.Ahora, sustituyamos
T
por 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*p
tiene tipoT
que es tipoR [10]
, por lo quesizeof *p
es equivalente a losizeof (T)
que es equivalente asizeof (R [10])
. Así que estamos asignando suficiente espacio para una matrizN
por10
elemento deR
.Podemos llevar esto aún más lejos si queremos; supongamos que
R
es en sí mismo un tipo de matrizint [5]
. Sustituye esoR
y obtenemosLo mismo pasa -
sizeof *p
es la misma quesizeof (int [10][5])
, y que terminan asignando un trozo contiguo de memoria lo suficientemente grande como para contener unaN
por10
por5
variedad 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. Sip
es 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
p
apunta 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
T
con el tipo de matrizR [10]
:Una diferencia inmediatamente aparente; estamos desreferenciando explícitamente
p
antes de aplicar el operador de subíndice. No queremos subíndices enp
, queremos subíndices en lo quep
apunta (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ícitamentep
con*
. Pero recuerde que desde arriba*p
es lo mismo quep[0]
, por lo que podemos sustituirlo poro solo
Por lo tanto, si
p
apunta a una matriz 2D, podemos indexar en esa matriz de la siguientep
manera:Llevando esto a la misma conclusión anterior y sustituyéndolo
R
porint [5]
:Esto funciona igual si
p
apunta 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