web-dev-qa-db-fra.com

Pourquoi l'utilisation de alloca () n'est-elle pas considérée comme une bonne pratique?

alloca() alloue de la mémoire sur la pile plutôt que sur le tas, comme dans le cas de malloc(). Donc, quand je reviens de la routine, la mémoire est libérée. En fait, cela résout mon problème de libération de mémoire allouée dynamiquement. La libération de la mémoire allouée via malloc() est un problème majeur. Si elle est manquée, elle entraîne toutes sortes de problèmes de mémoire.

Pourquoi l'utilisation de alloca() est-elle déconseillée malgré les caractéristiques ci-dessus?

347
Vaibhav

La réponse se trouve juste là dans la page man (au moins sur Linux ):

VALEUR RENVOYÉE La fonction alloca () renvoie un pointeur au début du espace alloué. Si la causes d'allocation débordement de pile, le comportement du programme n'est pas défini.

Ce qui ne veut pas dire qu'il ne devrait jamais être utilisé. L'un des projets OSS sur lequel je travaille l'utilise beaucoup, et tant que vous n'en abusez pas (alloca 'énormes valeurs), tout va bien. Une fois passé la marque "quelques centaines d'octets", il est temps d'utiliser malloc et amis, à la place. Vous pouvez toujours avoir des échecs d'allocation, mais au moins vous aurez une indication de l'échec au lieu de simplement vider la pile.

206
Sean Bright

L'un des bugs les plus mémorables que j'ai eu était lié à une fonction inline qui utilisait alloca. Il s'est manifesté par un débordement de pile (car il alloue sur la pile) à des moments aléatoires de l'exécution du programme.

Dans le fichier d'en-tête:

void DoSomething() {
   wchar_t* pStr = alloca(100);
   //......
}

Dans le fichier d'implémentation:

void Process() {
   for (i = 0; i < 1000000; i++) {
     DoSomething();
   }
}

Ainsi, la fonction DoSomething du compilateur en ligne et toutes les allocations de pile se produisaient dans la fonction Process(), ce qui a pour effet de faire exploser la pile. Pour ma défense (et ce n’est pas moi qui ai trouvé le problème, j’ai dû faire appel à l’un des développeurs principaux lorsque je ne pouvais pas résoudre le problème), ce n’était pas directement alloca, c’était l’une des chaînes ATL macros de conversion.

La leçon est donc la suivante: n'utilisez pas alloca dans des fonctions que vous croyez être en ligne.

181
Igor Zevaka

Ancienne question mais personne n’a mentionné qu’elle devrait être remplacée par des tableaux de longueur variable.

char arr[size];

au lieu de

char *arr=alloca(size);

C'est dans le standard C99 et existait en tant qu'extension de compilateur dans de nombreux compilateurs.

65
Patrick Schlüter

alloca () est très utile si vous ne pouvez pas utiliser une variable locale standard car sa taille doit être déterminée au moment de l'exécution et vous pouvez absolument garantir que le pointeur que vous obtenez de alloca () ne sera JAMAIS utilisé après le retour de cette fonction.

Vous pouvez être assez sûr si vous

  • ne retournez pas le pointeur ou tout ce qui le contient. 
  • ne stockez pas le pointeur dans une structure allouée sur le tas
  • ne laissez aucun autre fil utiliser le pointeur

Le véritable danger vient de la possibilité que quelqu'un d'autre viole ces conditions plus tard. En gardant cela à l’esprit, c’est bien pour passer des tampons aux fonctions qui formatent le texte en eux :)

53
Arthur Ulfeldt

Comme indiqué dans cet article de newsgroup , il existe plusieurs raisons pour lesquelles utiliser alloca peut être considéré comme difficile et dangereux:

  • Tous les compilateurs ne prennent pas en charge alloca.
  • Certains compilateurs interprètent différemment le comportement souhaité de alloca. La portabilité n'est donc pas garantie, même entre les compilateurs qui le prennent en charge.
  • Certaines implémentations sont buggées.
38
FreeMemory

Un problème est que ce n'est pas standard, bien qu'il soit largement pris en charge. Toutes choses étant égales par ailleurs, j'utiliserais toujours une fonction standard plutôt qu'une extension commune du compilateur.

25
David Thornley

encore allouer l'utilisation est découragée, pourquoi?

Je ne perçois pas un tel consensus. Beaucoup de pros forts; quelques inconvénients:

  • C99 fournit des tableaux de longueur variable, qui seraient souvent utilisés de manière préférentielle car la notation est plus cohérente avec les tableaux de longueur fixe et intuitive en général
  • beaucoup de systèmes ont moins de mémoire/d'espace d'adressage disponible pour la pile que pour le tas, ce qui rend le programme légèrement plus sensible à l'épuisement de la mémoire (par débordement de pile): cela peut être considéré comme une bonne ou une mauvaise chose - une L’une des raisons pour lesquelles la pile ne croît pas automatiquement comme le tas le fait est d’empêcher que des programmes hors de contrôle aient autant d’impact négatif sur l’ensemble de la machine
  • lorsqu'elle est utilisée dans une étendue plus locale (telle qu'une boucle while ou for) ou dans plusieurs étendues, la mémoire s'accumule par itération/étendue et n'est pas libérée tant que la fonction n'est pas sortie: ceci contraste avec les variables normales définies dans l'étendue d'une structure de contrôle. (Par exemple, for {int i = 0; i < 2; ++i) { X } accumulerait la mémoire alloca- demandée sous X, mais la mémoire d'un tableau de taille fixe serait recyclée par itération).
  • les compilateurs modernes ne font généralement pas les fonctions inline qui appellent alloca, mais si vous les forcez, le alloca se produira dans le contexte de l'appelant (c'est-à-dire que la pile ne sera libérée qu'au retour de l'appelant).
  • alloca est passé d'une fonctionnalité non-portable à une extension standardisée, mais une certaine perception négative peut persister
  • la durée de vie est liée à la portée de la fonction, ce qui convient ou non au programmeur mieux que le contrôle explicite de malloc
  • devoir utiliser malloc incite à penser à la désallocation. Si elle est gérée via une fonction d'encapsulation (par exemple, WonderfulObject_DestructorFree(ptr)), elle fournit un point de départ pour les opérations de nettoyage de l'implémentation (telles que la fermeture de descripteurs de fichiers, la libération de pointeurs internes ou la journalisation) sans modifications explicites au code client: c’est parfois un modèle agréable à adopter systématiquement
    • dans ce style de programmation pseudo-OO, il est naturel de vouloir quelque chose comme WonderfulObject* p = WonderfulObject_AllocConstructor(); - ce qui est possible lorsque le "constructeur" est une fonction renvoyant une mémoire malloc- (car la mémoire reste allouée après que la fonction a retourné la valeur à stocker dans p) , mais pas si le "constructeur" utilise alloca
      • une version macro de WonderfulObject_AllocConstructor pourrait permettre d'atteindre cet objectif, mais les "macros sont néfastes" en ce sens qu'elles peuvent entrer en conflit les unes avec les autres et avec du code non macro, et créer des substitutions inattendues et des problèmes difficiles à diagnostiquer par la suite.
    • les opérations free manquantes peuvent être détectées par ValGrind, Purify, etc., mais les appels "destructeurs" manquants ne peuvent pas toujours être détectés - un avantage très ténu en termes d'application de l'utilisation prévue; Certaines implémentations de alloca() (telles que GCC) utilisent une macro intégrée pour alloca(), de sorte que le remplacement à l'exécution d'une bibliothèque de diagnostics utilisant la mémoire n'est pas possible de la même manière que pour malloc/realloc/free (par exemple, une clôture électrique)
  • certaines implémentations ont des problèmes subtils: par exemple, à partir de la page de manuel Linux:</ p>

    Alloca () ne peut pas être utilisé dans la liste des arguments d'un appel de fonction, car l'espace de pile réservé par alloca () apparaîtrait dans la pile au milieu de l'espace réservé aux arguments de la fonction.</ li> </ ul>


    Je sais que cette question est étiquetée C, mais en tant que programmeur C++, je pensais utiliser C++ pour illustrer l'utilité potentielle de alloca: le code ci-dessous (et ici à ideone ) crée un vecteur traçant des types polymorphes de tailles différentes sont alloués à la pile (avec la durée de vie liée au retour de la fonction) plutôt que alloués au tas.

    #include <alloca.h>
    #include <iostream>
    #include <vector>
    
    struct Base
    {
        virtual ~Base() { }
        virtual int to_int() const = 0;
    };
    
    struct Integer : Base
    {
        Integer(int n) : n_(n) { }
        int to_int() const { return n_; }
        int n_;
    };
    
    struct Double : Base
    {
        Double(double n) : n_(n) { }
        int to_int() const { return -n_; }
        double n_;
    };
    
    inline Base* factory(double d) __attribute__((always_inline));
    
    inline Base* factory(double d)
    {
        if ((double)(int)d != d)
            return new (alloca(sizeof(Double))) Double(d);
        else
            return new (alloca(sizeof(Integer))) Integer(d);
    }
    
    int main()
    {
        std::vector<Base*> numbers;
        numbers.Push_back(factory(29.3));
        numbers.Push_back(factory(29));
        numbers.Push_back(factory(7.1));
        numbers.Push_back(factory(2));
        numbers.Push_back(factory(231.0));
        for (std::vector<Base*>::const_iterator i = numbers.begin();
             i != numbers.end(); ++i)
        {
            std::cout << *i << ' ' << (*i)->to_int() << '\n';
            (*i)->~Base();   // optionally / else Undefined Behaviour iff the
                             // program depends on side effects of destructor
        }
    }
    
21
Tony Delroy

Toutes les autres réponses sont correctes. Cependant, si la chose que vous voulez affecter à l'aide de alloca() est relativement petite, je pense que c'est une bonne technique plus rapide et plus pratique que d'utiliser malloc() ou autrement.

En d'autres termes, alloca( 0x00ffffff ) est dangereux et susceptible de provoquer un débordement, exactement autant que char hugeArray[ 0x00ffffff ];. Soyez prudent et raisonnable et tout ira bien.

12
JSBձոգչ

Tout le monde a déjà souligné le gros problème qui est le comportement indéfini potentiel d'un débordement de pile, mais il convient de mentionner que l'environnement Windows dispose d'un excellent mécanisme pour intercepter cela à l'aide d'exceptions structurées (SEH) et de pages de garde. Etant donné que la pile ne croît que si nécessaire, ces pages de garde se trouvent dans des zones non allouées. Si vous y allouez (en débordant la pile), une exception est levée.

Vous pouvez intercepter cette exception SEH et appeler _resetstkoflw pour réinitialiser la pile et continuer votre chemin joyeux. Ce n’est pas idéal, mais c’est un autre mécanisme qui permet au moins de savoir que quelque chose a mal tourné lorsque le contenu a été traité. * nix pourrait avoir quelque chose de similaire dont je ne suis pas au courant.

Je recommande de limiter la taille maximale d'allocation en encapsulant alloca et en effectuant un suivi en interne. Si vous étiez vraiment assidu à ce sujet, vous pourriez placer certaines sentinelles en haut de votre fonction pour suivre toutes les allocations allouées dans la portée de la fonction et vérifier son intégrité par rapport au montant maximum autorisé pour votre projet.

En outre, en plus de ne pas permettre les fuites de mémoire, alloca ne provoque pas de fragmentation de la mémoire, ce qui est très important. Je ne pense pas que alloca soit une mauvaise pratique si vous l'utilisez intelligemment, ce qui est fondamentalement vrai pour tout. :-)

11
SilentDirge

Beaucoup de réponses intéressantes à cette "vieille" question, même des réponses relativement nouvelles, mais je n'en ai trouvé aucune qui mentionne cela ...

Lorsqu’il est utilisé correctement et avec précaution, utilisez systématiquement alloca() (peut-être à l'échelle de l'application) pour gérer de petites allocations de longueur variable (ou les C99 VLA, le cas échéant) peuvent conduire à une réduction de la pile globale croissance qu’une implémentation par ailleurs équivalente utilisant surdimensionné matrices locales de longueur fixe. Donc, alloca() peut êtrebon pour votre pilesi vous l'utilisez avec précaution.

J'ai trouvé cette citation dans ... OK, j'ai inventé cette citation. Mais vraiment, pensez-y ...

@j_random_hacker a très raison dans ses commentaires sous d'autres réponses: éviter l'utilisation de alloca() en faveur de tableaux locaux surdimensionnés ne rend pas votre programme plus sûr contre les débordements de pile (sauf si votre compilateur est suffisamment vieux pour autoriser l'inlining de fonctions qui utilisent alloca() dans lequel cas vous devriez mettre à niveau, ou sauf si vous utilisez alloca() à l'intérieur des boucles, auquel cas vous ne devriez pas ... utiliser alloca() à l'intérieur des boucles).

J'ai travaillé sur des environnements de bureau/serveur et des systèmes intégrés. Beaucoup de systèmes embarqués n'utilisent pas du tout de tas (ils ne lient même pas de support), pour des raisons qui incluent la perception que la mémoire allouée dynamiquement est mauvaise en raison des risques de fuites de mémoire sur une application qui ne se lâche jamais. jamais redémarrage pendant des années, ou la justification plus raisonnable que la mémoire dynamique est dangereuse, car il ne peut pas être certain qu’une application ne fragmentera jamais son tas au point d’épuiser complètement sa mémoire. Donc, les programmeurs intégrés se retrouvent avec peu d'alternatives.

alloca() (ou VLA) peut être le bon outil pour le travail.

J'ai vu maintes et maintes fois un programmeur créer un tampon alloué par pile "assez grand pour gérer tous les cas possibles". Dans un arbre d’appel profondément imbriqué, l’utilisation répétée de ce modèle (anti-?) Entraîne une utilisation exagérée de la pile. (Imaginez un arbre d’appel d’une profondeur de 20 niveaux. À chaque niveau, pour des raisons différentes, la fonction sur-alloue aveuglément un tampon de 1024 octets "par mesure de sécurité" alors qu’en général, il en utilisera 16 ou moins, des cas rares peuvent en utiliser davantage.) Une alternative consiste à utiliser alloca() ou VLA et à n'allouer que la quantité d'espace de pile nécessaire à votre fonction pour éviter de surcharger inutilement la pile. Espérons qu'une des fonctions de l'arborescence des appels nécessite une allocation supérieure à la normale, que d'autres utilisent encore leurs petites allocations normales et que l'utilisation globale de la pile d'applications est bien moindre que si chaque fonction sur-allouait aveuglément un tampon local. .

Mais si vous choisissez d'utiliser alloca()...

Sur la base des autres réponses de cette page, il semble que les VLA devraient être sûrs (ils ne composent pas les allocations de pile s’ils sont appelés depuis une boucle), mais si vous utilisez alloca(), veillez à ne pas l’utiliser dans une boucle, et assurez-vous que votre fonction ne peut pas être insérée s'il y a une chance qu'elle soit appelée dans la boucle d'une autre fonction.

9
phonetagger

Voici pourquoi:

char x;
char *y=malloc(1);
char *z=alloca(&x-y);
*z = 1;

Personne n’écrirait ce code, mais l’argument de taille auquel vous passez alloca provient presque certainement d’une sorte d’entrée, ce qui pourrait permettre de manière malveillante de faire en sorte que votre programme alloca soit aussi énorme. Après tout, si la taille n’est pas basée sur l’entrée ou n’a pas la possibilité d’être grande, pourquoi n’avez-vous pas simplement déclaré un petit tampon local de taille fixe?

Pratiquement tout le code utilisant alloca et/ou C99 vlas contient des bugs sérieux qui peuvent entraîner des plantages (si vous êtes chanceux) ou un compromis sur les privilèges (si vous ne l'êtes pas aussi).

9
R..

alloca () est gentil et efficace ... mais il est aussi profondément brisé.

  • comportement de portée brisé (portée de fonction au lieu de portée de bloc)
  • use inconsistant with malloc ( alloca () - le pointeur ne devrait pas être libéré, vous devez désormais savoir d'où viennent les pointeurs de free () uniquement ceux que vous avez obtenus avec malloc () )
  • mauvais comportement lorsque vous utilisez également inline (la portée passe parfois à la fonction d'appelant selon que l'appelant est en ligne ou non). 
  • pas de contrôle de limite de pile
  • comportement indéfini en cas d'échec (ne renvoie pas la valeur NULL comme malloc ... et que signifie échec car il ne vérifie pas les limites de la pile de toute façon ...)
  • pas ansi standard

Dans la plupart des cas, vous pouvez le remplacer en utilisant des variables locales et une taille majeure. Si vous l'utilisez pour des objets volumineux, il est généralement préférable de les placer sur le tas. 

Si vous en avez vraiment besoin, vous pouvez utiliser VLA (pas de vla en C++, dommage). Ils sont bien meilleurs que alloca () en ce qui concerne le comportement et la cohérence de la portée. Comme je le voisVLAsont une sorte de alloca () rendu juste. 

Bien sûr, une structure locale ou un tableau utilisant un majorant de l'espace nécessaire est toujours préférable, et si vous ne disposez pas d'une telle allocation de tas majeure avec plain malloc (), c'est probablement sain d'esprit. Je ne vois aucun cas d'utilisation sensé dans lequel vous avez vraiment besoin de alloca () ou VLA.

9
kriss

Un emplacement où alloca() est particulièrement dangereux par rapport à malloc() est le noyau - le noyau d'un système d'exploitation typique possède un espace de pile de taille fixe codé en dur dans l'un de ses en-têtes; ce n'est pas aussi flexible que la pile d'une application. Passer un appel à alloca() avec une taille non justifiée peut provoquer le crash du noyau . Certains compilateurs mettent en garde sur l'utilisation de alloca() (et même sur les VLA) sous certaines options qui devraient être activées lors de la compilation du code du noyau - ici, il est préférable d'allouer de la mémoire dans le tas qui n'est pas fixée par une limite codée en dur.

7
Sondhi Chakraborty

Je ne pense pas que quiconque ait mentionné cela: l'utilisation de alloca dans une fonction entravera ou désactivera certaines optimisations qui pourraient autrement être appliquées à la fonction, car le compilateur ne peut pas connaître la taille du cadre de pile de la fonction. 

Par exemple, une optimisation courante par les compilateurs C consiste à éliminer l’utilisation du pointeur de cadre dans une fonction; les accès aux cadres sont effectués par rapport au pointeur de pile; donc il y a encore un registre pour usage général. Mais si alloca est appelé dans la fonction, la différence entre sp et fp sera inconnue pour une partie de la fonction; cette optimisation ne peut donc pas être effectuée.

Compte tenu de la rareté de son utilisation et de son statut ombragé en tant que fonction standard, les concepteurs de compilateurs désactivent probablement toute optimisation qui (pourrait} _ causer des problèmes avec alloca, si cela demanderait plus qu'un petit effort pour le faire fonctionner avec alloca. 

4
greggo

Un écueil avec alloca est que longjmp le rembobine.

Autrement dit, si vous enregistrez un contexte avec setjmp, puis alloca de la mémoire, puis longjmp dans le contexte, vous risquez de perdre la mémoire alloca (sans le moindre avis). Le pointeur de pile est de retour à sa place et la mémoire n'est plus réservée; si vous appelez une fonction ou effectuez une autre alloca, vous écraserez la alloca originale.

Pour clarifier, ce à quoi je fais référence ici est une situation dans laquelle longjmp ne sort pas de la fonction où la alloca a eu lieu! Au lieu de cela, une fonction enregistre le contexte avec setjmp; puis alloue de la mémoire avec alloca et finalement un longjmp a lieu dans ce contexte. La mémoire alloca de cette fonction n'est pas entièrement libérée; juste toute la mémoire allouée depuis la setjmp. Bien sûr, je parle d'un comportement observé; aucune exigence de ce genre n’est documentée à propos de alloca que je connaisse. 

La documentation met généralement l’accent sur le concept selon lequel la mémoire alloca est associée à une activation function , et non à un bloc; que plusieurs invocations de alloca récupèrent simplement plus de mémoire de pile, qui est entièrement libérée à la fin de la fonction. Pas si; la mémoire est en fait associée au contexte de la procédure. Lorsque le contexte est restauré avec longjmp, il en va de même de l'état alloca précédent. C'est une conséquence du fait que le registre de pointeur de pile est lui-même utilisé pour l'allocation, et également (nécessairement) sauvegardé et restauré dans le jmp_buf.

Incidemment, cela, si cela fonctionne de cette façon, fournit un mécanisme plausible pour libérer délibérément la mémoire allouée avec alloca.

Je me suis heurté à cela comme la cause première d'un bogue.

4
Kaz

Si vous écrivez accidentellement au-delà du bloc alloué avec alloca (en raison d'un débordement de mémoire tampon par exemple), vous écraserez l'adresse return de votre fonction, car celle-ci est située "au-dessus" de la pile, c'est-à-dire après votre bloc alloué.

 _alloca block on the stack

La conséquence de ceci est double:

  1. Le programme se plantera de manière spectaculaire et il sera impossible de dire pourquoi ni où il s’est écrasé (la pile se déroulera probablement à une adresse aléatoire en raison du pointeur de cadre écrasé).

  2. Cela rend le débordement de mémoire tampon beaucoup plus dangereux, puisqu'un utilisateur malveillant peut créer une charge utile spéciale qui serait mise sur la pile et peut donc être exécutée.

En revanche, si vous écrivez au-delà d'un bloc sur le tas, vous obtenez "juste" une corruption de tas. Le programme se terminera probablement de manière inattendue mais déroulera la pile correctement, réduisant ainsi le risque d'exécution de code malveillant.

4
rustyx

Malheureusement, la alloca() vraiment géniale est absente du TCC presque génial. Gcc a alloca().

  1. Il sème le germe de sa propre destruction. Avec return comme destructeur.

  2. Comme malloc(), il renvoie un pointeur non valide en cas d'échec, ce qui segfault sur les systèmes modernes dotés d'un MMU (et, espérons-le, redémarre ceux qui n'en possèdent pas).

  3. Contrairement aux variables automatiques, vous pouvez spécifier la taille au moment de l'exécution.

Cela fonctionne bien avec la récursivité. Vous pouvez utiliser des variables statiques pour obtenir quelque chose de similaire à la récursion finale et n'utiliser que quelques autres informations de transmission à chaque itération.

Si vous appuyez trop profondément, vous êtes assuré d'une erreur de segmentation (si vous avez un MMU).

Notez que malloc() n'offre plus rien car il renvoie NULL (qui sera également différent si assigné) lorsque le système manque de mémoire. C'est à dire. tout ce que vous pouvez faire, c'est cautionner ou simplement essayer de l'assigner de quelque façon.

Pour utiliser malloc(), j'utilise des globales et leur attribue la valeur NULL. Si le pointeur n'est pas NULL, je le libère avant d'utiliser malloc().

Vous pouvez également utiliser realloc() comme cas général si vous souhaitez copier des données existantes. Vous devez vérifier le pointeur avant de savoir si vous allez copier ou concaténer après la realloc().

3.2.5.2 Avantages de alloca

4
zagam

Les processus ne disposent que d’une quantité limitée d’espace disponible, beaucoup moins que la quantité de mémoire disponible pour malloc().

En utilisant alloca(), vous augmentez considérablement vos chances d'obtenir une erreur Stack Overflow (si vous avez de la chance, ou un crash inexplicable si vous ne l'êtes pas).

3
RichieHindle

En fait, il n'est pas garanti que alloca utilise la pile ..__ En effet, l'implémentation gcc-2.95 de alloca alloue de la mémoire à partir du tas à l'aide de malloc. De plus, cette implémentation est boguée, elle peut entraîner une fuite de mémoire et un comportement inattendu si vous l'appelez à l'intérieur d'un bloc avec une utilisation ultérieure de goto. Non, pas pour dire que vous ne devriez jamais l’utiliser, mais parfois, alloca entraîne plus de frais généraux qu’il n’en retire.

2
user7491277

Pas très joli, mais si les performances importent vraiment, vous pouvez préallouer de l’espace sur la pile.

Si vous disposez déjà de la taille maximale du bloc de mémoire qui vous convient et que vous souhaitez conserver les contrôles de débordement, vous pouvez procéder comme suit:

void f()
{
    char array_on_stack[ MAX_BYTES_TO_ALLOCATE ];
    SomeType *p = (SomeType *)array;

    (...)
}
2
Sylvain Rodrigue

La fonction alloca est excellente et tous les opposants répandent simplement le FUD. 

void foo()
{
    int x = 50000; 
    char array[x];
    char *parray = (char *)alloca(x);
}

Les tableaux et parray sont EXACTEMENT les mêmes avec EXACTEMENT les mêmes risques. Dire que l'un est meilleur qu'un autre est un choix syntaxique et non technique.

En ce qui concerne le choix des variables de pile par rapport aux variables de segment de mémoire, il existe de nombreux avantages à exécuter des programmes longs en utilisant pile sur segment de mémoire pour les variables ayant une durée de vie prédéfinie. Vous évitez la fragmentation de tas et vous pouvez éviter de développer votre espace de processus avec un espace de tas inutilisé (inutilisable). Vous n'avez pas besoin de le nettoyer. Vous pouvez contrôler l'allocation de pile sur le processus. 

Pourquoi est-ce mauvais? 

1
mlwmohawk

IMHO, alloca est considéré comme une mauvaise pratique parce que tout le monde a peur d'épuiser la limite de taille de pile.

J'ai beaucoup appris en lisant ce fil et quelques autres liens:

J'utilise alloca principalement pour rendre mes fichiers simples C compilables sur msvc et gcc sans aucune modification, style C89, sans #ifdef _MSC_VER, etc.

Je vous remercie ! Ce fil m'a fait m'inscrire à ce site :)

1
ytoto

À mon avis, alloca (), le cas échéant, ne devrait être utilisé que de manière contraignante. Tout comme l’utilisation de "goto", un grand nombre de personnes, par ailleurs raisonnables, ont une forte aversion non seulement pour l'utilisation de, mais aussi pour l'existence de, alloca ().

Pour une utilisation intégrée, lorsque la taille de la pile est connue et que des limites peuvent être imposées via une convention et une analyse de la taille de l'allocation, et que le compilateur ne peut pas être mis à niveau pour prendre en charge C99 +, l'utilisation de alloca () convient parfaitement. connu pour l'utiliser.

Lorsqu'ils sont disponibles, les VLA peuvent présenter certains avantages par rapport à alloca (): le compilateur peut générer des contrôles de limite de pile qui captureront les accès hors limites lorsque l'accès par style de tableau est utilisé (je ne sais pas si les compilateurs le font, mais cela peut se faire. être fait), et l’analyse du code peut déterminer si les expressions d’accès au tableau sont correctement délimitées. Notez que, dans certains environnements de programmation, tels que l'automobile, les équipements médicaux et l'avionique, cette analyse doit être effectuée même pour des matrices de taille fixe, à la fois automatiques (sur la pile) et statiques (globale ou locale).

Sur les architectures qui stockent à la fois des données et des adresses de retour/pointeurs de trame sur la pile (d'après ce que je sais, ce sont toutes), toute variable allouée à une pile peut être dangereuse car l'adresse de la variable peut être prise et des valeurs d'entrée non contrôlées peuvent le permettre. toutes sortes de méfaits.

La portabilité est moins une préoccupation dans l'espace intégré, mais c'est un bon argument contre l'utilisation de alloca () en dehors de circonstances soigneusement contrôlées.

En dehors de l'espace intégré, j'ai utilisé alloca () principalement dans les fonctions de journalisation et de formatage pour améliorer l'efficacité, ainsi que dans un analyseur lexical non récursif, dans lequel des structures temporaires (allouées à l'aide alloca () sont créées lors de la tokenisation et de la classification, puis object (alloué via malloc ()) est rempli avant le retour de la fonction. L'utilisation de alloca () pour les plus petites structures temporaires réduit considérablement la fragmentation lorsque l'objet persistant est alloué.

1
Daniel Glasser

La plupart des réponses ici manquent en grande partie le problème: il existe une raison pour laquelle utiliser _alloca() est potentiellement pire que de simplement stocker de gros objets dans la pile.

La principale différence entre le stockage automatique et _alloca() est que celui-ci souffre d'un problème supplémentaire (grave): le bloc alloué est non contrôlé par le compilateur , le compilateur n'a donc aucun moyen de l'optimiser ou de le recycler.

Comparer:

while (condition) {
    char buffer[0x100]; // Chill.
    /* ... */
}

avec:

while (condition) {
    char* buffer = _alloca(0x100); // Bad!
    /* ... */
}

Le problème avec ce dernier devrait être évident.

0
alecov

Je ne pense pas que quiconque l'ait mentionné, mais alloca a également de sérieux problèmes de sécurité que malloc ne présente pas nécessairement (même si ces problèmes se posent également avec n'importe quel tableau basé sur une pile, dynamique ou non). Étant donné que la mémoire est allouée sur la pile, les débordements/débordements de mémoire tampon ont des conséquences beaucoup plus graves que le simple malloc.

En particulier, l'adresse de retour d'une fonction est stockée dans la pile. Si cette valeur est corrompue, votre code pourrait être assigné à une région de mémoire exécutable. Les compilateurs font de leur mieux pour rendre cela difficile (en particulier en randomisant la disposition des adresses). Cependant, cela est clairement pire qu’un simple dépassement de capacité de la pile car le meilleur des cas est un SEGFAULT si la valeur de retour est corrompue, mais il peut également commencer à exécuter une partie aléatoire de la mémoire ou, dans le pire des cas, une région de mémoire qui compromet la sécurité de votre programme .

0
Dyllon Gagnier