¿Cómo evitar que las matrices de longitud variable se estrelle cuando no hay suficiente memoria?

StackOverflow https://stackoverflow.com/questions/4151172

  •  08-10-2019
  •  | 
  •  

Pregunta

Antes de que se apoyaron las matrices de longitud variable, me gustaría asignar dinámicamente como esto:

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 que puede ahora hacer que se vea más limpia:

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

Pero ahora no tengo "sin memoria" de cheques. De hecho, el programa se bloquea si n es demasiado grande.

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

¿Fue útil?

Solución

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 ha sido - No Recurse demasiado profundamente, y no asignar estructuras de datos muy grandes con el tiempo de almacenamiento automático (seguir utilizando malloc() para estos casos). El valor de "muy grande" depende fuertemente de su entorno.

En otras palabras, no declara int array[n]; menos que sepa que n está limitada a un valor razonable, de modo que usted habría estado dispuesto a declarar una matriz de ese tamaño máximo como una matriz de tipo ordinario, no modificadas de forma variable .

(Sí, esto significa que de forma variable modificada con matrices de tipo no son tan útiles como parecen a primera vista, ya que obtiene muy poco durante acaba de declarar la matriz en el tamaño máximo es necesario).

Otros consejos

Se puede evitar que se estrelle por no usarlos. :)

Con toda seriedad, casi no hay manera segura de consumir matrices de longitud variable para hacer su vida más fácil a menos que tenga fuertes límites 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);

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

Otra oscuro caso donde VLA pueden ser útiles en algoritmos recursivos donde se sabe que el número total de entradas de matriz requeridos en todos los niveles de recursividad está delimitada por n, donde n es lo suficientemente pequeño que esté seguro de que lo hará sin desbordamiento del pila, pero donde puede haber hasta niveles n de recursividad y los niveles individuales que utilizan elementos hasta n. Antes de C99, la única manera de manejar este caso sin tomar espacio de pila n^2 era utilizar malloc. Con VLA, se puede resolver el problema por completo en la pila.

Tenga en cuenta, estos casos en los que VLA son realmente beneficiosos son muy muy raro. Normalmente VLA es sólo una manera de engañar a sí mismo que la gestión de memoria es fácil, hasta que llegue poco a los resultantes (trivial-a-exploit) vulnerabilidades 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;
}

En realidad se trata de un costo prohibitivo para comprobar fuera de las condiciones de memoria en todas partes. La manera de tratar enterprisy masiva de datos es a tamaños de datos mediante la definición de límite de la tapa dura del tamaño en un solo punto de control temprano y fallar rápido y con gracia cuando la tapa se ve afectado.

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top