web-dev-qa-db-fra.com

Différence entre le type de tableau et le tableau alloué avec malloc

Aujourd'hui, j'aidais un de mes amis avec du code C et j'ai découvert un comportement étrange que je ne pouvais pas lui expliquer pourquoi cela se produisait. Nous avions un fichier TSV avec une liste d’entiers, avec un entier par ligne. La première ligne était le nombre de lignes que la liste avait.

Nous avons également eu un fichier c avec un "readfile" très simple. La première ligne a été lue à n, le nombre de lignes, puis il y a eu une initialisation de:

int list[n]

et enfin une boucle for de n avec un fscanf.

Pour les petits n (jusqu'à environ 100 000), tout allait bien. Cependant, nous avons constaté que si n était gros (10 ^ 6), un segfault se produirait.

Enfin, nous avons modifié l’initialisation de la liste en

int *list = malloc(n*sizeof(int))

et tout quand bien, même avec très grand n.

Quelqu'un peut-il expliquer pourquoi cela s'est produit? qu'est-ce qui causait le segfault avec int list [n], qui a été arrêté lorsque nous commençons à utiliser list = malloc (n * sizeof (int))?

58
Jorge Leitão

Il y a plusieurs pièces différentes en jeu ici.

La première est la différence entre déclarer un tableau comme

int array[n];

et

int* array = malloc(n * sizeof(int));

Dans la première version, vous déclarez un objet avec une durée de stockage automatique. Cela signifie que le tableau ne vit que tant que la fonction qui l'appelle existe. Dans la deuxième version, vous obtenez de la mémoire avec une durée de stockage dynamique, ce qui signifie qu’elle existera jusqu’à ce qu’elle soit explicitement désallouée avec free.

La raison pour laquelle la deuxième version fonctionne ici est un détail d'implémentation de la manière dont C est généralement compilé. En règle générale, la mémoire C est divisée en plusieurs régions, y compris la pile (pour les appels de fonction et les variables locales) et le segment de mémoire (pour les objets malloced). La pile a généralement une taille beaucoup plus petite que le tas; généralement, c'est quelque chose comme 8MB. Par conséquent, si vous essayez d’allouer un grand tableau avec

int array[n];

Vous pourriez alors dépasser l'espace de stockage de la pile, ce qui causerait le segfault. D'autre part, le tas a généralement une taille énorme (disons, autant d'espace libre que sur le système), et donc mallocun objet volumineux ne provoquera pas une erreur de mémoire insuffisante.

En général, soyez prudent avec les tableaux de longueurs variables en C. Ils peuvent facilement dépasser la taille de la pile. Préférez malloc à moins que vous ne sachiez que la taille est petite ou que vous ne voulez vraiment que le tableau pendant une courte période.

J'espère que cela t'aides! 

133
templatetypedef
int list[n]

Alloue de l'espace pour n entiers sur la pile , qui est généralement assez petit. L’utilisation de la mémoire sur la pile est beaucoup plus rapide que l’alternative, mais elle est assez petite et il est facile de déborder de la pile (c’est-à-dire d’allouer trop de mémoire) si vous faites des choses comme allouer des tableaux énormes ou faire une récursion trop profonde. Vous n'avez pas besoin de désallouer manuellement la mémoire allouée de cette façon, cela est fait par le compilateur lorsque le tableau sort de la portée.

malloc d'autre part alloue de l'espace dans le tas , qui est généralement très grand par rapport à la pile. Vous devrez allouer une quantité de mémoire beaucoup plus grande sur le tas pour l'épuiser, mais il est beaucoup plus lent à allouer de la mémoire sur le tas que sur la pile, et vous devez la désallouer manuellement via free lorsque vous avez terminé. il.

8
Seth Carnegie

int list [n] stocke les données dans la pile, tandis que malloc les stocke dans le tas.

La pile est limitée et il n'y a pas beaucoup d'espace, tandis que le tas est beaucoup plus gros.

2
Asier Gutierrez

int list[n] est un VLA, qui alloue sur la pile plutôt que sur le tas. Vous n'avez pas besoin de le libérer (il se libère automatiquement à la fin de l'appel de fonction) et il alloue rapidement, mais l'espace de stockage est très limité, comme vous l'avez découvert. Vous devez allouer des valeurs plus grandes sur le tas.

1
Puppy

Cette déclaration alloue de la mémoire sur la pile

    int list[n]

malloc alloue sur le tas.

La taille de la pile est généralement inférieure à celle du tas. Par conséquent, si vous allouez trop de mémoire sur la pile, vous obtenez un stackoverflow. 

Voir aussi cette réponse pour plus d'informations

1
thumbmunkeys

En supposant que vous ayez une implémentation typique dans votre implémentation, il est fort probable que:

int list[n]

liste allouée sur votre pile, où:

int *list = malloc(n*sizeof(int))

alloué de la mémoire sur votre tas. 

Dans le cas d'une pile, la taille de celles-ci peut généralement croître (si elles peuvent croître du tout). Dans le cas d'un tas, il y a toujours une limite, mais elle a tendance à être très largement et (largement) contrainte par votre espace d'adressage RAM + swap +, qui est généralement au moins d'un ordre de grandeur supérieur, voire plus.

1
Flexo
   int array[n];

C'est un exemple de tableau alloué statiquement et au moment de la compilation, la taille du tableau sera connue. Et le tableau sera alloué sur la pile.

   int *array(malloc(sizeof(int)*n);

C'est un exemple de tableau alloué dynamiquement et la taille du tableau sera connue de l'utilisateur au moment de l'exécution. Et le tableau sera alloué sur le tas.

0
cammando

Si vous êtes sur linux, vous pouvez définir ulimit -s sur une valeur plus grande, ce qui pourrait également fonctionner pour l'allocation de pile. ..__ Lorsque vous allouez de la mémoire sur pile, cette mémoire reste jusqu'à la fin de l'exécution de votre fonction. Si vous allouez de la mémoire sur le tas (à l'aide de malloc), vous pouvez libérer la mémoire à tout moment (même avant la fin de l'exécution de votre fonction).

Généralement, heap devrait être utilisé pour les allocations de mémoire importantes.

0
Manik Sidana

Lorsque vous allouez à l'aide de malloc, la mémoire est allouée à partir du tas et non de la pile, dont la taille est beaucoup plus limitée.

0
Tibor