Tengo un programa que lee una lista "en bruto" de entidades en el juego, y tengo la intención de hacer una matriz que contenga un número de índice (int) de un número indeterminado de entidades, para procesar varias cosas. Me gustaría evitar usar demasiada memoria o CPU para mantener tales índices ...
Una solución rápida y sucia que uso hasta ahora es declarar, en la función de procesamiento principal (foco local), la matriz con un tamaño de las entidades máximas del juego y otro número entero para realizar un seguimiento de cuántos se han agregado a la lista. Esto no es satisfactorio, ya que cada lista contiene más de 3000 matrices, lo que no es mucho, pero se siente como un desperdicio, ya que posiblemente usaré la solución para 6-7 listas para diversas funciones.
No he encontrado ninguna solución específica de C (no C ++ o C #) para lograr esto. Puedo usar punteros, pero tengo un poco de miedo de usarlos (a menos que sea la única forma posible).
Las matrices no abandonan el ámbito de la función local (deben pasarse a una función y luego descartarse), en caso de que eso cambie las cosas.
Si los punteros son la única solución, ¿cómo puedo hacer un seguimiento de ellos para evitar fugas?
fuente
Respuestas:
Si necesita una matriz dinámica, no puede escapar de los punteros. ¿Por qué tienes miedo? No morderán (siempre y cuando tengas cuidado, eso es). No hay una matriz dinámica incorporada en C, solo tendrá que escribir una usted mismo. En C ++, puede usar la
std::vector
clase integrada . C # y casi todos los demás lenguajes de alto nivel también tienen alguna clase similar que gestiona matrices dinámicas para usted.Si planea escribir el suyo, aquí hay algo para comenzar: la mayoría de las implementaciones de matriz dinámica funcionan comenzando con una matriz de algún tamaño predeterminado (pequeño), luego, cuando se quede sin espacio al agregar un nuevo elemento, duplique tamaño de la matriz. Como puede ver en el ejemplo a continuación, no es nada difícil: (he omitido los controles de seguridad por brevedad)
Usarlo es igual de simple:
fuente
removeArray
método que elimine el último elemento también sería bueno. Si lo permite, lo agregaré a su código de muestra.Hay un par de opciones en las que puedo pensar.
array[100]
sin tener que caminar1-99
primero. Y puede que tampoco sea tan útil para usted usar.Es difícil decir qué opción sería la mejor en su situación. Simplemente crear una gran matriz es, por supuesto, una de las soluciones más fáciles y no debería darle muchos problemas a menos que sea realmente grande.
fuente
realloc
con el n. ° 3: asigne la matriz a un tamaño normal y luego créelo cada vez que se agote.realloc
se encargará de copiar sus datos si es necesario. En cuanto a la pregunta del OP sobre la gestión de la memoria, solo necesitamalloc
una vez al comienzo,free
una vez al final yrealloc
cada vez que se quede sin espacio. No es tan malo.7 * 3264 * 32 bit
suena como91.39 kilobytes
. No es mucho por ningún estándar en estos días;)realloc
regresaNULL
:a->array = (int *)realloc(a->array, a->size * sizeof(int));
... Tal vez hubiera sido mejor escrito como:int *temp = realloc(a->array, a->size * sizeof *a->array); a->array = temp;
... De esa manera, sería obvio que lo que suceda debe suceder antes elNULL
valor se asigna aa->array
(si es que lo es).Al igual que con todo lo que parece más aterrador al principio que después, ¡la mejor manera de superar el miedo inicial es sumergirse en la incomodidad de lo desconocido ! A veces es lo que más aprendemos, después de todo.
Lamentablemente, hay limitaciones. Mientras todavía está aprendiendo a usar una función, no debe asumir el papel de un maestro, por ejemplo. A menudo leo las respuestas de aquellos que aparentemente no saben cómo usarlos
realloc
(es decir, ¡ la respuesta actualmente aceptada! ) Y les digo a los demás cómo usarlos incorrectamente, ocasionalmente con el pretexto de que han omitido el manejo de errores , a pesar de que este es un error común. que necesita mención Aquí hay una respuesta que explica cómo usarrealloc
correctamente . Tenga en cuenta que la respuesta es almacenar el valor de retorno en una variable diferente para realizar la comprobación de errores.Cada vez que llama a una función, y cada vez que usa una matriz, está usando un puntero. Las conversiones están ocurriendo implícitamente, lo que si algo debería ser aún más aterrador, ya que son las cosas que no vemos que a menudo causan la mayoría de los problemas. Por ejemplo, pérdidas de memoria ...
Los operadores de matriz son operadores de puntero.
array[x]
es realmente un atajo para*(array + x)
, que se puede dividir en:*
y(array + x)
. Es muy probable que eso*
sea lo que te confunda. Podemos eliminar aún más la suma del problema suponiendox
que se convierte0
, por lo tanto,array[0]
en*array
que la suma0
no cambiará el valor ...... y así podemos ver que
*array
es equivalente aarray[0]
. Puede usar uno donde quiera usar el otro, y viceversa. Los operadores de matriz son operadores de puntero.malloc
,realloc
y los amigos no inventan el concepto de puntero que has estado usando todo el tiempo; simplemente usan esto para implementar alguna otra característica, que es una forma diferente de duración de almacenamiento, más adecuada cuando desea cambios drásticos y dinámicos en el tamaño .Es una pena que la respuesta actualmente aceptada también vaya en contra de otros consejos muy bien fundados sobre StackOverflow y, al mismo tiempo, pierda la oportunidad de introducir una característica poco conocida que brilla exactamente para este caso de uso: matriz flexible miembros! Esa es en realidad una respuesta bastante rota ... :(
Cuando defina su
struct
, declare su matriz al final de la estructura, sin ningún límite superior. Por ejemplo:¡Esto le permitirá unir su matriz
int
en la misma asignación que la suyacount
, y hacer que se unan así puede ser muy útil !sizeof (struct int_list)
actuará como sivalue
tuviera un tamaño de 0, por lo que le dirá el tamaño de la estructura con una lista vacía . Aún necesita agregar al tamaño pasadorealloc
para especificar el tamaño de su lista.Otro consejo útil es recordar que
realloc(NULL, x)
es equivalente amalloc(x)
, y podemos usar esto para simplificar nuestro código. Por ejemplo:La razón que elegí para usar
struct int_list **
como primer argumento puede no parecer obvia de inmediato, pero si piensas en el segundo argumento, cualquier cambio realizadovalue
desde adentropush_back
no sería visible para la función desde la que estamos llamando, ¿verdad? Lo mismo ocurre con el primer argumento, y debemos ser capaces de modificar nuestroarray
, no solo aquí, sino posiblemente también en cualquier otra función que lo pasamos a ...array
comienza apuntando a la nada; Es una lista vacía. Inicializarlo es lo mismo que agregarle. Por ejemplo:PD: ¡ Recuerda
free(array);
cuando hayas terminado con eso!fuente
array[x]
es realmente un atajo para*(array + x)
, [...]" ¿Estás seguro de eso ???? Vea una exposición de sus diferentes comportamientos: eli.thegreenplace.net/2009/10/21/… .array[index]
es en realidadptr[index]
disfrazada ... "La definición del operador subíndice[]
es queE1[E2]
es idéntica a(*((E1)+(E2)))
" No se puede refutar la stdint main(void) { unsigned char lower[] = "abcdefghijklmnopqrstuvwxyz"; for (size_t x = 0; x < sizeof lower - 1; x++) { putchar(x[lower]); } }
... Probablemente necesites#include <stdio.h>
y<stddef.h>
... ¿Ves cómo escribíx[lower]
(x
siendo el tipo entero) en lugar delower[x]
? Al compilador de C no le importa, porque*(lower + x)
es el mismo valor que*(x + lower)
, ylower[x]
es el primero donde-comox[lower]
es el último. Todas estas expresiones son equivalentes. Pruébelos ... compruébelo usted mismo, si no puede creer mi palabra ...gcc
oclang
para toda su compilación de C, porque encontrará que hay muchos paquetes que han adoptado las características de C99 ...Sobre la base del diseño de Matteo Furlans , cuando dijo que "la mayoría de las implementaciones de matriz dinámica funcionan comenzando con una matriz de un tamaño predeterminado (pequeño), luego cada vez que se queda sin espacio al agregar un nuevo elemento, duplica el tamaño de la matriz ". La diferencia en el " trabajo en progreso " a continuación es que no duplica su tamaño, apunta a usar solo lo que se requiere. También he omitido las verificaciones de seguridad por simplicidad ... También basándose en la idea de brimboriums , he intentado agregar una función de eliminación al código ...
El archivo storage.h se ve así ...
El archivo storage.c se ve así ...
El main.c se ve así ...
Esperamos con interés la crítica constructiva a seguir ...
fuente
malloc()
antes de intentar usar la asignación. En la misma línea, es un error asignar directamente el resultado delrealloc()
puntero a la memoria original que se reasigna; sirealloc()
falla,NULL
se devuelve y el código se deja con una pérdida de memoria. Es mucho más eficiente duplicar la memoria al cambiar el tamaño que agregar 1 espacio a la vez: menos llamadas arealloc()
.int
, etc.) a la vez. Duplicar es una solución típica, pero no creo que haya una solución óptima que se adapte a todas las circunstancias. Aquí es por qué duplicar es una buena idea (algún otro factor como 1.5 también estaría bien): si comienza con una asignación razonable, es posible que no necesite reasignar en absoluto. Cuando se necesita más memoria, la asignación razonable se duplica, y así sucesivamente. De esta manera, es probable que solo necesite una o dos llamadasrealloc()
.Cuando estas diciendo
básicamente estás diciendo que estás usando "punteros", pero uno que es un puntero local de toda la matriz en lugar de un puntero de toda la memoria. Dado que conceptualmente ya está utilizando "punteros" (es decir, números de identificación que se refieren a un elemento en una matriz), ¿por qué no simplemente utiliza punteros regulares (es decir, números de identificación que se refieren a un elemento en la matriz más grande: toda la memoria )
En lugar de que sus objetos almacenen números de identificación de recursos, puede hacer que almacenen un puntero. Básicamente lo mismo, pero mucho más eficiente ya que evitamos convertir "array + index" en un "puntero".
Los punteros no dan miedo si los considera como un índice de matriz para toda la memoria (que es lo que realmente son)
fuente
Para crear una matriz de elementos ilimitados de cualquier tipo:
Y cómo usarlo:
Este vector / matriz puede contener cualquier tipo de elemento y tiene un tamaño completamente dinámico.
fuente
Bueno, supongo que si necesita eliminar un elemento, hará una copia de la matriz despreciando el elemento que se excluirá.
Suponga eso
getElement2BRemove()
,copy2TempVector( void* ...)
yfillFromTempVector(...)
son métodos auxiliares para manejar el vector temporal.fuente