Erreur de segmentation sur de grandes tailles de baie

116

Le code suivant me donne une erreur de segmentation lorsqu'il est exécuté sur une machine 2 Go, mais fonctionne sur une machine 4 Go.

int main()
{
   int c[1000000];
   cout << "done\n";
   return 0;
}

La taille du tableau n'est que de 4 Mo. Y a-t-il une limite sur la taille d'un tableau qui peut être utilisé en C ++?

Mayank
la source

Réponses:

130

Vous obtenez probablement un débordement de pile ici. Le tableau est trop grand pour tenir dans l'espace d'adressage de la pile de votre programme.

Si vous allouez le tableau sur le tas, tout va bien, en supposant que votre machine dispose de suffisamment de mémoire.

int* array = new int[1000000];

Mais rappelez-vous que cela vous obligera à utiliser delete[]le tableau. Une meilleure solution serait de l'utiliser std::vector<int>et de le redimensionner à 1000000 éléments.

Charles Salvia
la source
3
Merci pour la réponse, mais pouvez-vous m'expliquer pourquoi les tableaux sont alloués sur la pile et pourquoi pas dans la mémoire principale du programme.
Mayank
18
Le code donné est alloué sur la pile car il est spécifié sous forme de tableau avec un nombre constant d'éléments au moment de la compilation. Les valeurs ne sont mises sur le tas qu'avec malloc, new, etc.
Seth Johnson
6
Toutes les variables automatiques sont allouées sur la pile. Si vous regardez le disasseble, vous verrez la taille de vos variables locales soustraite du pointeur de pile. Lorsque vous appelez malloc ou calloc ou l'une des fonctions de mémoire, les fonctions recherchent des blocs de mémoire suffisamment grands pour satisfaire votre demande.
rediffusé
@Charles pourquoi nous pourrions allouer plus de mémoire à partir du tas, pas de la pile? d'après ma compréhension, la pile et le tas se déplacent dans une direction opposée dans l'espace d'adressage alloué dans la mémoire.
saurabh agarwal
2
@saurabhagarwal Le tas ne bouge pas. Ce n'est même pas une région mémoire contiguë. L'allocateur renvoie simplement un bloc de mémoire libre qui correspond à votre exigence de taille. Que sont et où se trouvent la pile et le tas?
phuclv
56

En C ou C ++, les objets locaux sont généralement alloués sur la pile. Vous allouez un grand tableau sur la pile, plus que ce que la pile peut gérer, vous obtenez donc un stackoverflow.

Ne l'allouez pas localement sur la pile, utilisez plutôt un autre endroit. Cela peut être réalisé en rendant l'objet global ou en l'allouant sur le tas global . Les variables globales sont très bien, si vous n'utilisez le depuis aucune autre unité de compilation. Pour vous assurer que cela ne se produit pas par accident, ajoutez un spécificateur de stockage statique, sinon utilisez simplement le tas.

Cela allouera dans le segment BSS, qui fait partie du tas:

static int c[1000000];
int main()
{
   cout << "done\n";
   return 0;
}

Cela allouera dans le segment DATA, qui fait également partie du tas:

int c[1000000] = {};
int main()
{
   cout << "done\n";
   return 0;
}

Cela allouera à un emplacement non spécifié dans le tas:

int main()
{
   int* c = new int[1000000];
   cout << "done\n";
   return 0;
}
Gunther Piez
la source
Si vous utilisez le troisième modèle, en allouant sur le tas, n'oubliez pas de supprimer [] le pointeur à un moment donné ou vous perdrez de la mémoire. Ou regardez dans les pointeurs intelligents.
davidA
8
@meowsqueak Bien sûr, c'est une bonne pratique de deletepartout où vous allouez new. Mais si vous êtes sûr d'allouer de la mémoire une seule fois (comme dans main), ce n'est strictement pas nécessaire - la mémoire est garantie d'être libérée à la sortie de main même sans explicite delete.
Gunther Piez
'at'drhirsch (comment faites-vous un at-caractère de toute façon?) - oui, commentaire juste. Comme le PO semble nouveau dans le langage, je voulais simplement m'assurer qu'eux-mêmes, ainsi que quiconque voyant votre bonne réponse, étaient conscients des implications de la troisième option si elle est utilisée de manière générale.
davidA
15

De plus, si vous utilisez la plupart des systèmes UNIX et Linux, vous pouvez augmenter temporairement la taille de la pile à l'aide de la commande suivante:

ulimit -s unlimited

Mais attention, la mémoire est une ressource limitée et avec une grande puissance viennent de grandes responsabilités :)

RSFalcon7
la source
1
C'est la solution mais je conseille à tous d'être extrêmement prudent lors de la suppression de ces limites par défaut sur la taille de la pile du programme. Vous rencontrerez non seulement une baisse de performances sévère, mais votre système pourrait tomber en panne. Par exemple, j'ai essayé de trier un tableau avec 16 000 000 éléments entiers avec tri rapide sur une machine avec 4 Go de RAM et mon système a été presque tué. LOL
rbaleksandar
@rbaleksandar Je pense que le programme ~ 16 Mo a presque tué votre machine parce que vous travailliez avec plusieurs copies du tableau (peut-être une par appel de fonction?) Essayez une implémentation plus consciente de la mémoire;)
RSFalcon7
Je suis à peu près sûr que la gestion du tableau est correcte puisque je passe par référence et non par valeur. La même chose se produit avec le tri à bulles. Bon sang, même si mon implémentation de tri rapide est nul, le tri est quelque chose que vous ne pouvez pas implémenter de manière incorrecte. LOL
rbaleksandar
LOL, vous pouvez essayer le tri radix, ou simplement utiliser std :: sort :)
RSFalcon7
1
Aucune chance. C'est une mission de laboratoire. : D
rbaleksandar
3

Votre tableau est alloué sur la pile dans ce cas, essayez d'allouer un tableau de la même taille en utilisant alloc.

rediffusion
la source
3

Parce que vous stockez le tableau dans la pile. Vous devez le stocker dans le tas. Consultez ce lien pour comprendre le concept du tas et de la pile.

Narek
la source