web-dev-qa-db-fra.com

Dans quelles circonstances malloc peut-il retourner NULL?

Cela ne m'est jamais arrivé et je programme depuis des années maintenant.

Quelqu'un peut-il me donner un exemple d'un programme non trivial dans lequel malloc ne fonctionnera pas réellement?

je ne parle pas d'épuisement de la mémoire: Je cherche le cas simple lorsque vous allouez un seul bloc de mémoire dans une taille liée donnée par l'utilisateur, disons un entier, provoque malloc pour échouer.

28
RanZilber

Oui.

Essayez simplement de malloc plus de mémoire que ce que votre système peut fournir (soit en épuisant votre espace d'adressage, soit en mémoire virtuelle - la plus petite des deux).

malloc(SIZE_MAX)

le fera probablement. Sinon, répétez plusieurs fois jusqu'à épuisement.

17
Useless

Vous devez faire un peu de travail dans les systèmes embarqués, vous y retournerez fréquemment NULL :-)

Il est beaucoup plus difficile de manquer de mémoire dans les systèmes modernes d'espace d'adressage massif et de stockage de sauvegarde, mais cela est tout à fait possible dans les applications où vous traitez de grandes quantités de données, telles que les SIG ou les bases de données en mémoire, ou dans des endroits où votre le code buggy entraîne une fuite de mémoire.

Mais cela n'a vraiment pas d'importance si vous ne l'avez jamais vécu auparavant - la norme dit que cela peut se produire, vous devez donc vous en occuper. Je n'ai pas non plus été heurté par une voiture au cours des dernières décennies, mais cela ne signifie pas que je me promène sur les routes sans regarder en premier.

Et re votre édition:

Je ne parle pas d'épuisement de la mémoire, ...

le très définition d'épuisement de la mémoire est malloc ne vous donnant pas l'espace souhaité. Peu importe que cela soit dû à l'allocation de toute la mémoire disponible ou à la fragmentation du tas, ce qui signifie que vous ne pouvez pas obtenir un bloc contigu même si l'agrégat de tous les blocs libres dans l'arène mémoire est plus élevé, ou limiter artificiellement votre utilisation de l'espace d'adressage en utilisant la fonction conforme aux normes :

void *malloc (size_t sz) { return NULL; }

Le standard C ne fait pas de distinction entre modes d'échec, mais seulement qu'il réussit ou échoue.

23
paxdiablo

Tout programme écrit en c qui a besoin d'allouer dynamiquement plus de mémoire que l'OS ne le permet actuellement.

Pour le plaisir, si vous utilisez ubuntu, saisissez

 ulimit -v 5000

Tout programme que vous exécutez se bloquera très probablement (en raison d'une défaillance de malloc) car vous avez limité la quantité de mémoire disponible à un processus à une quantité limitée.

9
RussS

À moins que votre mémoire ne soit déjà complètement réservée (ou fortement fragmentée), la seule façon pour que malloc() retourne un pointeur NULL- est de demander un espace de taille zéro:

char *foo = malloc(0);

Citant la norme C99, §7.20.3, sous-section 1:

Si la taille de l'espace demandé est nulle, le comportement est défini par l'implémentation: soit un pointeur nul est renvoyé, soit le comportement est comme si la taille était une valeur non nulle, sauf que le pointeur renvoyé ne doit pas être utilisé pour accéder à un objet.

En d'autres termes, malloc(0) peut renvoyer un pointeur NULL- ou un pointeur valide vers zéro octet alloué.

8
Philip

Consultez simplement la page de manuel de malloc.

En cas de succès, un pointeur sur le bloc mémoire alloué par la fonction.
Le type de ce pointeur est toujours vide *, qui peut être converti en le type de pointeur de données souhaité afin d'être déréférencable.
Si la fonction n'a pas pu allouer le bloc de mémoire demandé, un pointeur nul est renvoyé.

4
starrify

Sur un système plus ou moins standard, en utilisant un malloc standard à un paramètre, il existe trois modes de défaillance possibles (auxquels je peux penser):

1) La taille de l'allocation demandée n'est pas autorisée. Par exemple, certains systèmes peuvent ne pas autoriser une allocation> 16M, même si davantage de stockage est disponible.

2) Une zone libre contiguë de la taille demandée, avec une limite par défaut, ne peut pas être située dans le tas. Il peut y avoir encore beaucoup de tas, mais pas assez en un seul morceau.

3) Le tas total alloué a dépassé une limite "artificielle". Par exemple, l'utilisateur peut être interdit d'allouer plus de 100M, même s'il y a 200M gratuits et disponibles pour le "système" dans un seul tas combiné.

(Bien sûr, vous pouvez obtenir des combinaisons de 2 et 3, car certains systèmes allouent des blocs d'espace d'adressage non contigus au tas au fur et à mesure de sa croissance, en plaçant la "limite de taille de tas" sur le total des blocs.)

Notez que certains environnements prennent en charge des paramètres malloc supplémentaires tels que l'alignement et l'ID de pool qui peuvent ajouter leurs propres rebondissements.

4
Hot Licks

Choisissez n'importe quelle plate-forme, bien que l'embarqué soit probablement plus facile. malloc (ou new) une tonne de RAM (ou fuite RAM au fil du temps ou même le fragmenter en utilisant naïf) algorithmes). Boom. malloc ne retourne NULL pour moi à l'occasion quand de "mauvaises" choses se produisent.

En réponse à votre montage. Oui encore. La fragmentation de la mémoire au fil du temps peut faire en sorte que même une seule allocation d'un int puisse échouer. Gardez également à l'esprit que malloc ne se contente pas d'allouer 4 octets pour un int, mais peut prendre autant d'espace qu'il le souhaite. Il a ses propres trucs de tenue de livres et prend souvent 32 à 64 octets au minimum.

3
Michael Dorgan

Puisque vous avez demandé un exemple, voici un programme qui verra (éventuellement) malloc retourner NULL:

perror();void*malloc();main(){for(;;)if(!malloc(999)){perror(0);return 0;}}

Quelle? Vous n'aimez pas le code délibérément obscurci? ;) (S'il fonctionne pendant quelques minutes et ne plante pas sur votre machine, tuez-le, remplacez 999 Par un plus grand nombre et réessayez.)

EDIT: Si cela ne fonctionne pas, quelle que soit la taille du nombre, alors ce qui se passe, c'est que votre système dit "Voici de la mémoire!" mais tant que vous n'essayez pas de l'utiliser, il n'est pas alloué. Dans quel cas:

perror();char*p;void*malloc();main(){for(;;){p=malloc(999);if(p)*p=0;else{perror(0);return 0;}}

Devrait faire l'affaire. Si nous pouvons utiliser des extensions GCC, je pense que nous pouvons le réduire encore plus en changeant char*p;void*malloc(); en void*p,*malloc(); mais si vous vouliez vraiment jouer au golf, vous seriez sur le Code Golf SE.

3
Chris Lutz

Oui. Malloc renverra NULL lorsque la bibliothèque noyau/système sera certaine qu'aucune mémoire ne peut être allouée.

La raison pour laquelle vous ne voyez généralement pas cela sur les machines modernes est que Malloc n'alloue pas vraiment de mémoire, mais qu'il demande plutôt un "espace d'adressage virtuel" réservé à votre programme afin que vous puissiez y écrire. Les noyaux tels que Linux moderne sont réellement sur-validés, c'est-à-dire qu'ils vous permettent d'allouer plus de mémoire que votre système ne peut réellement fournir (swap + RAM) tant que cela tient dans l'espace d'adressage du système (généralement 48 bits sur des plates-formes 64 bits, IIRC) . Ainsi, sur ces systèmes, vous déclencherez probablement un tueur OOM avant de déclencher le retour d'un pointeur NULL. Un bon exemple est un 512 Mo RAM dans une machine 32 bits: il est trivial d'écrire un programme C qui sera mangé par le tueur OOM car il essaie de malloc tous les disponibles RAM + échange.

(La surcompensation peut être désactivée au moment de la compilation sous Linux, donc cela dépend des options de construction si un noyau Linux donné se surchargera ou non. Cependant, les noyaux de distribution de bureau le font.)

1
user268396