¿Cuáles son las desventajas de las matrices dinámicas sobre las matrices tradicionales en lenguajes como C / C ++?

Las matrices estáticas son de tamaño fijo. Las matrices dinámicas son de tamaño variable y, por lo tanto, son más flexibles. Esta flexibilidad tiene un costo.

Las matrices fijas tienen O (1) inserción, eliminación y lectura. También son trivialmente fáciles de implementar (¡es parte del lenguaje!), Son completamente predecibles con el tiempo y son muy conscientes de la memoria caché. Por lo tanto, al elegir una implementación de matriz alternativa, debe preguntar qué va a renunciar. Eso probablemente consistirá en renunciar a una combinación de:

  1. Eficiencia de la memoria
  2. Eficiencia de la CPU
  3. Caché
  4. Facilidad de implementación (esto no se puede descartar …)
  5. Costo de lecturas
  6. Costo de escrituras
  7. Costo de eliminaciones
  8. Previsibilidad del costo a lo largo del tiempo (a medida que mi matriz dinámica crece, ¿cómo va a cambiar su consumo de memoria / CPU de una operación a otra)?

Hay más factores dependiendo de lo que esté haciendo (p. Ej., Simplemente agregue “paralelizabilidad” o “distribuido” a cada uno de estos para obtener un conjunto completamente nuevo de requisitos, y luego algunos …)

Por supuesto, la mayor compensación con la que se enfrenta cuando se utilizan la mayoría de las implementaciones de matriz dinámica es la memoria. Cuando haya llenado su matriz, debe seguir creciendo, por lo que la matriz debe asignar más memoria. A menudo, una clase de vector estándar asignará una gran cantidad de memoria que nunca usará. Dado que los cambios de tamaño generalmente ocurren de manera transparente cuando agrega un elemento más de lo que la matriz puede admitir, significa que una sola operación de inserción puede ser significativamente más costosa que otra.

STD :: vector de STL es probablemente la implementación más extendida de arreglos dinámicos en C ++. Aunque los vectores son objetos de matriz dinámica, también tienen la propiedad interesante de que garantizan la contigüidad de la memoria para permitir que los punteros iteren sobre ellos de la misma manera que iterarían sobre una matriz estática. Esta garantía hace que el cambio de tamaño sea especialmente doloroso y potencialmente más costoso, ya que podría tener que copiar la matriz existente si no puede simplemente extender el espacio asignado existente (aunque personalmente no estoy muy claro sobre los detalles de cómo se implementa el cambio de tamaño) ) Ver: http://stackoverflow.com/questio…

Si está interesado en leer más, Wikipedia también tiene referencias a algunas estructuras más interesantes (aunque no necesariamente de uso popular) implementaciones de matrices dinámicas, como árboles hash y vectores de varios niveles: http: //en.wikipedia. org / wiki / Dyn …

La ventaja habitual es que las matrices tradicionales son un poco más eficientes en términos de asignación / desasignación.

Sin embargo, antes de comentar más sobre eso, vale la pena señalar que esa ventaja se está erosionando gradualmente. Considera este código:

  int f (int x) {
   int r;
   int * p = nuevo int [x];  // Tamaño dinámico!
   p [0] = 42;
   p [9] = x;
   r = p [0] * p [9];
   eliminar [] p;
   volver r;
 }

Si compila eso con Clang en un alto nivel de optimización (en x86–64), obtendrá este código:

  f (int): # @f (int)
  imul eax, edi, 42
  jubilado

¡Observe cómo la asignación de matriz dinámica se ha optimizado por completo!

El compilador aún no puede hacer eso en la mayoría de los casos, pero espero que mejore cada vez más en el tratamiento de casos en los que puede igualar una asignación y su correspondiente desasignación (un cambio de idioma algo reciente permite optimizar tales casos sin realmente llamar al función de asignación).

Suponiendo que lleguemos a un punto en el que los optimizadores sean realmente buenos con eso, todavía hay una diferencia interesante entre las declaraciones de matriz tradicionales y las alternativas de matriz dinámica: el operador sizeof para las matrices tradicionales realmente produce el tamaño del almacenamiento de la matriz y lo hace como una constante expresión.

PD: Para ser perfectamente claro: la función f en el ejemplo anterior no pretende reflejar buenas prácticas de codificación (es un código terrible ). Solo pretende ilustrar lo que pueden hacer los optimizadores de C ++ de última generación.

La mayor desventaja es el rendimiento en algunas operaciones como insertar, dependiendo de cómo implemente la matriz dinámica.

La implementación más común es tener internamente una matriz más una capacidad inicial, y manejar la operación de crecimiento de la matriz de manera transparente. Cuando se agrega un elemento más allá de la capacidad inicial, se crea una nueva matriz más grande; mientras que el anterior se copia al nuevo antes de agregar el nuevo elemento. Esto mantiene las lecturas O (1) como con una matriz, pero agregar un elemento más allá de la capacidad inicial podría ser O (N) (lineal) ya que cada elemento en la matriz debe copiarse en una nueva matriz.

Por lo tanto, el beneficio es tener una gestión automática del tamaño de la matriz, pero la principal desventaja es el rendimiento de una inserción después de elementos N + 1; donde N es la capacidad inicial (si internamente se implementa con matrices).

Si se implementa como una lista vinculada, la adición o eliminación de elementos, así como la exploración, se convierten en O (N) lineal en lugar de O (1) como con una matriz.

Hay otras desventajas, pero son más un caso de borde. Por ejemplo, si tiene una matriz que es demasiado grande, agregar N + 1 duplicará la asignación de memoria y potencialmente tendrá un desbordamiento inesperado:
Una matriz que utiliza 1.0 Gb de memoria, cuando un proceso de 32 bits permite 2 Gb de asignación máxima por proceso, por lo que el inserto N + 1 intenta crear y copiar una matriz nueva de 1.X Gb y se queda sin memoria.

También podría perder la posibilidad de utilizar la aritmética de puntero como con las matrices.