2010-11-11 17 views
9

Antes fueron apoyados matrices de longitud variable, que dinámicamente gustaría asignarlos como esto:¿Cómo se evita que las matrices de longitud variable se bloqueen cuando no hay suficiente memoria?

int foo(size_t n) 
{ 
    int *arr = malloc(n * sizeof int); 
    if (!arr) return ENOMEM; /* not enough memory */ 
    . 
    . else do stuff with arr[] 
    . 
    free(arr); 
    return 0; 
} 

Con los arreglos de longitud variable ahora puedo hacer que se vea más limpia:

int bar(size_t n) 
{ 
    int arr[n]; 
    . 
    . do stuff with arr[] 
    . 
    return 0; 
} 

Pero ahora no tener una verificación "sin memoria". De hecho, el programa se bloquea si n es demasiado grande.

¿Cómo puedo fianza con gracia de la barra (n) si n es demasiado grande?

+0

Su límite puede (dependiendo del sistema, por supuesto) no ser "memoria" sino "tamaño de la pila". Y ahora la pregunta tiene más sentido para mí porque estaba pensando en tu implementación "anterior" en una caja de consumo razonablemente moderna y preguntándome * "¿Qué está haciendo que necesita una gran parte de un GB?" *. De todas formas. Si la pila te limita, la "vieja" forma puede ser mejor. – dmckee

+0

Esta es exactamente la razón por la que los VLA no se utilizan tanto (otro soporte deficiente en compiladores). C99 VLAs simplemente no son estables. – VSG24

Respuesta

12

la situación es exactamente sin cambios de cualquier otra variable locales - una declaración como esta:

int baz(void) 
{ 
    int arr[100000]; 
    . 
    . do stuff with arr[] 
    . 
    return 0; 
} 

tiene exactamente el mismo problema. La "solución" es la misma que siempre: no se repite demasiado, y no se asignan estructuras de datos muy grandes con una duración de almacenamiento automática (continúe usando malloc() para estos casos). El valor de "muy grande" depende en gran medida de su entorno.

En otras palabras, no declare int array[n]; a menos que sepa que n está limitado a un valor razonable, de modo que le hubiera gustado declarar una matriz de ese tamaño máximo como un tipo ordinario, no modificado de forma variable formación.

(Sí, esto significa que las matrices de tipos modificados de forma variable no son tan útiles como aparecen por primera vez, ya que gana muy poco más que solo declarar la matriz en el tamaño máximo necesario).

+0

Esto lo pone en perspectiva, gracias. Pude decidir el tamaño máximo y comenzar la función con 'if (n <= MAX) {int arr [n]; ...} ' – Henry

6

Puede evitar que se bloquee al no usarlos. :)

Con toda seriedad, casi no hay una forma segura de utilizar matrices de longitud variable para hacerte la vida más fácil a menos que tengas límites fuertes en el tamaño. Por otro lado, puede utilizarlos de forma condicional, en formas como esto:

char vla_buf[n < 1000 ? n : 1]; 
char *buf = sizeof vla_buf < n ? malloc(n) : vla_buf; 
if (!buf) goto error; 
/* ... Do stuff with buf ... */ 
if (buf != vla_buf) free(buf); 

Mientras que esto parece el dolor inútil, se puede hacer una gran diferencia de rendimiento, especialmente en aplicaciones con subprocesos, donde muchas llamadas a malloc y free podía resultado en contención de bloqueo. (Un beneficio notable de este truco es que se puede apoyar a los compiladores de edad sin VLA mediante la simple sustitución [n < 1000 ? n : 1] con 1000, por ejemplo, con una macro.)

Otro caso oscura donde VLA puede ser útil es en algoritmos recursivos donde se sabe que la el número total de entradas de matriz requeridas en todos los niveles de recursividad está limitado por n, donde n es lo suficientemente pequeño como para estar seguro de que no se desbordará la pila, pero donde podría haber hasta n niveles de recursión y niveles individuales que agotan a n elementos. Antes de C99, la única forma de manejar este caso sin tomar el espacio de pila n^2 era usar malloc. Con los VLA, puedes resolver el problema completamente en la pila.

Tenga en cuenta que estos casos en los que los VLA son realmente beneficiosos son muy raros. Normalmente, VLA es solo una manera de engañarse a sí mismo de que la administración de la memoria es fácil, hasta que se ve afectado por las vulnerabilidades resultantes (triviales para explotar) que ha creado.:-)

Editar: A la pregunta original, mejor dirección de OP:

#define MAX_VLA 10000 
int bar(size_t n) 
{ 
    int arr[n <= MAX_VLA ? n : 1]; 
    if (sizeof arr/sizeof *arr < n) return ENOMEM; 
    /* ... */ 
    return 0; 
} 
+0

En su primer ejemplo, también podría usar' [1000] 'en todas partes, ya que ya ha determinado que ese valor no (no) se bloqueará. El algoritmo recursivo parece ser el caso de uso real. – caf

+0

Me dio algunas buenas ideas, pero creo que la más sencilla es simplemente iniciar la función con 'if (n <= MAX_VLA) {int arr [n]; ...} '. Gracias. – Henry

+0

Tenga en cuenta que hay errores en sus ejemplos. :-) Debería ser 'if ((sizeof arr)/(sizeof int) Henry

0

En realidad se trata de un costo prohibitivo para comprobar fuera de las condiciones de memoria en todas partes. La manera emprendedora de manejar datos masivos es limitar el tamaño de los datos definiendo el límite máximo en el punto de control temprano individual y fallar rápido y con gracia cuando se golpea el límite.

Lo que acabo de sugerir es simple y estúpido. Pero es lo que siempre hace cualquier producto ordinario (no científico o especial). Y es lo que normalmente espera el cliente.

Cuestiones relacionadas