2010-11-11 6 views
9

Avant les tableaux de longueur variable ont été pris en charge, je les allouer dynamiquement comme ceci:Comment éviter que les tableaux de taille variable ne se bloquent quand la mémoire est insuffisante?

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; 
} 

Avec des tableaux de longueur variable, je peux maintenant le faire paraître plus propre:

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

Mais maintenant je n'a pas de vérification "out of memory". En fait, le programme se bloque si n est trop grand.

Comment puis-je gracier gracieusement de la barre (n) si n est trop grand?

+0

Votre limite peut (dépend du système, bien sûr) pas être "mémoire" mais "taille de la pile". Et maintenant la question a plus de sens pour moi parce que je pensais à votre "avant" la mise en œuvre sur une boîte de consommation raisonnablement moderne et je me demandais * "Que fait-il qu'il a besoin d'une grande partie d'un GB?" En tous cas. Si la pile vous limite, le "vieux" peut être meilleur. – dmckee

+0

C'est exactement la raison pour laquelle les VLA ne sont pas beaucoup utilisés (un autre étant un support médiocre dans les compilateurs). Les VLA C99 ne sont tout simplement pas stables. – VSG24

Répondre

12

La situation est exactement inchangé par rapport à toutes les autres variables locales - une déclaration comme celle-ci:

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

a exactement le même problème. La "solution" est la même qu'elle l'a toujours été - ne pas recurrencer trop profondément, et ne pas allouer de très grandes structures de données avec une durée de stockage automatique (continuer à utiliser malloc() pour ces cas). La valeur de "très grand" dépend fortement de votre environnement. En d'autres termes, ne déclarez pas int array[n];, sauf si vous savez que n est limité à une valeur raisonnable, de sorte que vous auriez été heureux de déclarer un tableau de cette taille maximale comme un type ordinaire, non modifié de manière variable tableau.

(Oui, cela signifie que les tableaux de type variablement modifiés ne sont pas aussi utiles qu'ils apparaissent au début, car vous gagnez très peu en déclarant simplement le tableau à la taille maximale requise).

+0

Cela met en perspective, merci. Je pourrais décider d'une taille maximum et commencer la fonction avec 'if (n <= MAX) {int arr [n]; ...} ' – Henry

6

Vous pouvez les empêcher de se bloquer en ne les utilisant pas. :)

Sérieusement, il n'y a presque aucun moyen sûr d'utiliser des tableaux de longueur variable pour vous rendre la vie plus facile, sauf si vous avez des limites fortes sur la taille. D'autre part, vous pouvez les utiliser sous condition, de manière à ceci:

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); 

Bien que cela ressemble à la douleur inutile, il peut faire une énorme différence de performances, en particulier dans les applications threadées où de nombreux appels à malloc et free pourrait entraîner un conflit de verrouillage. (Un autre avantage notable de cette astuce est que vous pouvez soutenir les anciens compilateurs sans VLA en remplaçant simplement [n < 1000 ? n : 1] avec 1000, par exemple avec une macro.)

Un autre cas obscur où VLA peut être utile est dans les algorithmes récursifs où vous connaissez les Le nombre total d'entrées de tableau requises pour tous les niveaux de récursivité est limité par n, où n est suffisamment petit pour que vous soyez certain qu'il ne débordera pas la pile, mais où il peut y avoir jusqu'à n niveaux de récursivité et niveaux individuels qui sont épuisés à n éléments. Avant C99, la seule façon de gérer ce cas sans prendre l'espace de pile n^2 était d'utiliser malloc. Avec les VLA, vous pouvez résoudre entièrement le problème sur la pile. Gardez à l'esprit, ces cas où les VLA sont vraiment bénéfiques sont assez rares. Normalement, VLA est juste un moyen de vous tromper que la gestion de la mémoire est facile, jusqu'à ce que vous soyez un peu par les vulnérabilités qui en résultent (trivial-à-exploiter) que vous avez créé.:-)

Edit: Pour mieux répondre question originale 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

Dans votre premier exemple, vous pourriez aussi bien utiliser' [1000] 'partout, puisque vous avez déjà déterminé que cette valeur ne devrait pas (ne devrait pas) planter. L'algorithme récursif semble être le cas d'utilisation réel. – caf

+0

Vous m'avez donné quelques bonnes idées, mais je pense que le plus simple est de simplement démarrer la fonction avec 'if (n <= MAX_VLA) {int arr [n]; ...} '. Merci. – Henry

+0

Notez qu'il y a des bogues dans vos exemples. :-) Il devrait être 'if ((sizeof arr)/(sizeof int) Henry

0

En réalité, il est extrêmement coûteux de vérifier les conditions de mémoire partout. La façon de traiter les données massives consiste à limiter la taille des données en définissant une limite de taille stricte à un point de contrôle unique et à échouer rapidement et avec élégance lorsque la limite est atteinte.

Ce que je viens de suggérer est simple et stupide. Mais c'est ce que fait chaque produit ordinaire (non scientifique ou spécial). Et c'est ce qui est normalement attendu par le client.