web-dev-qa-db-fra.com

C ++ Comment allouer dynamiquement de la mémoire sur la pile?

Existe-t-il un moyen d'allouer de la mémoire sur pile au lieu du tas? Je ne trouve pas un bon livre à ce sujet, quelqu'un ici a une idée?

31
Mark

Utilisez alloca() (parfois appelé _alloca() ou _malloca() ), mais soyez très prudent à ce sujet - il libère sa mémoire lorsque vous quittez une fonction, pas lorsque vous sortez du champ, donc vous exploserez rapidement si vous l'utilisez dans une boucle.

Par exemple, si vous avez une fonction comme

int foo( int nDataSize, int iterations ) 
{
   for ( int i = 0; i < iterations ; ++i )
   {
      char *bytes = alloca( nDataSize );
      // the memory above IS NOT FREED when we pass the brace below!
   } 
   return 0;
}  // alloca() memory only gets freed here

Ensuite, alloca () allouera un supplémentaire nDataSize octets à chaque fois dans la boucle. Aucun des octets alloca () n'est libéré jusqu'à ce que vous reveniez de la fonction. Donc, si vous avez un nDataSize de 1024 et un iterations de 8, vous allouerez 8 kilo-octets avant de revenir. Si vous avez un nDataSize = 65536 et iterations = 32768, vous allouerez un total de 65536 × 32768 = 2 147 483 648 octets, ce qui fera certainement exploser votre pile et provoquera un plantage.

anecdote: Vous pouvez facilement avoir des problèmes si vous écrivez après la fin du tampon, surtout si vous passez le tampon dans une autre fonction et que cette sous-fonction a la mauvaise idée de la longueur du tampon. J'ai une fois corrigé un bug plutôt amusant où nous utilisions alloca() pour créer un stockage temporaire pour le rendu d'un glyphe de police TrueType avant de l'envoyer dans la mémoire du GPU. Notre bibliothèque de polices ne tenait pas compte du diacritique dans le caractère suédois Å lors du calcul des tailles de glyphe, elle nous a donc dit d'allouer n octets pour stocker le glyphe avant le rendu, puis rendu réellement n + 128 octets. Les 128 octets supplémentaires ont écrit dans la pile d'appels, écrasant l'adresse de retour et provoquant un plantage non déterministe vraiment douloureux!

39
Crashworks

Comme il s'agit du tag C++, vous déclarez généralement les objets dont vous avez besoin dans la portée correcte. Ils sont alloués sur la pile et garantis pour être libérés à la sortie de la portée. C'est RAII , et un avantage critique de C++ sur C. Pas de mallocs ou news, et surtout pas de allocas, requis.

5
Steve Townsend

Vous pouvez déclarer un char[1024] Local ou le nombre d'octets que vous souhaitez (jusqu'à un certain point), puis prendre l'adresse du local pour un pointeur vers ce bloc de mémoire sur la pile. Pas exactement dynamique, mais vous pouvez ensuite terminer cette mémoire avec votre propre gestionnaire de mémoire si vous le souhaitez.

3
DuckMaestro

Article traitant de l'allocation dynamique de mémoire

Nous pouvons allouer dynamiquement un espace de longueur variable sur la mémoire de la pile en utilisant la fonction _alloca. Cette fonction alloue de la mémoire à partir de la pile de programmes. Cela prend simplement le nombre d'octets à allouer et retourne void * à l'espace alloué comme un appel malloc. Cette mémoire allouée sera libérée automatiquement à la sortie de la fonction.

Il n'est donc pas nécessaire de le libérer explicitement. Il faut garder à l'esprit la taille de l'allocation ici, car une exception de dépassement de pile peut se produire. La gestion des exceptions de dépassement de pile peut être utilisée pour de tels appels. En cas d'exception de dépassement de pile, on peut utiliser _resetstkoflw() pour le restaurer.

Donc, notre nouveau code avec _alloca serait :

int NewFunctionA()
{
   char* pszLineBuffer = (char*) _alloca(1024*sizeof(char));
    …..
  // Program logic
     ….
  //no need to free szLineBuffer
  return 1;
}
2
Sammy

Voir _malloca .

1
Dan

Lorsque/si C++ autorise l'utilisation de valeurs (non statiques) const pour les limites de tableau, ce sera plus facile.

Pour l'instant, le meilleur moyen que je connaisse est la récursivité. Il y a toutes sortes d'astuces intelligentes qui peuvent être faites, mais le plus simple que je sache est d'avoir votre routine déclarer un tableau de taille fixe, et remplir et opérer sur ce qu'il a. Quand c'est fait, s'il a besoin de plus d'espace pour finir, il s'appelle.

1
T.E.D.

Vous pouvez utiliser la bibliothèque BDE C++, par ex.

const int BUFFER_SIZE = 1024;
char      buffer[BUFFER_SIZE];

bdlma::BufferedSequentialAllocator allocator(buffer, BUFFER_SIZE);
bsl::vector<int>                   dataVector(&allocator);

dataVector.resize(50);

BDE fournit des options d'allocateur complètes ainsi que des collections comme bsl :: vector qui peuvent utiliser des allocateurs polymorphes sans changer le type du conteneur.

Vous pourriez également envisager:

1
JDiMatteo