Quel est l'intérêt de malloc (0)?

Réponses:

121

Selon les spécifications, malloc (0) renverra "un pointeur nul ou un pointeur unique qui peut être passé avec succès à free ()".

Cela vous permet en gros de n'allouer rien, mais de passer la variable "artist" à un appel à free () sans souci. Pour des raisons pratiques, c'est à peu près la même chose que de faire:

artist = NULL;
Reed Copsey
la source
51
Personnellement, je pense que définir sur NULL est une meilleure stratégie multiplateforme, car free () est garanti (par la spécification) pour fonctionner correctement sur NULL en entrée.
Reed Copsey
2
Comme mentionné par C. Ross, certaines plates-formes, techniquement, pourraient renvoyer un pointeur ici (qui est un "pointeur unique qui peut être passé à free"), mais si vous traitez cela comme un char *, cela peut vous donner un caractère invalide et non terminé. Il pourrait être dangereux de se fier à cela dans des situations multiplateformes.
Reed Copsey
9
Je souhaite vraiment que les spécifications indiquent également "transmis en toute sécurité à la réallocation" -.-
hanshenrik
1
@NSAddict "structure vide où sizeof retournera 0", veuillez fournir un exemple, sonne comme une extension de langage.
chux - Réintégrer Monica le
1
@hanshenrik Qui a dit que vous ne pouviez pas? realloc()vous permet de passer tout pointeur valide renvoyé par malloc(). Cela devrait suffire.
glglgl
51

La norme C (C17 7.22.3 / 1) dit:

Si la taille de l'espace demandé est zéro, 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 différente de zéro, sauf que le pointeur retourné ne doit pas être utilisé pour accéder à un objet.

Donc, malloc(0)pourrait retourner NULLou un pointeur valide qui ne peut pas être déréférencé . Dans les deux cas, il est parfaitement valable de faire appel free()à lui.

Je ne pense pas vraiment malloc(0)avoir beaucoup d'utilité, sauf dans les cas où il malloc(n)est appelé dans une boucle par exemple, et npeut être nul.

En regardant le code dans le lien, je pense que l'auteur avait deux idées fausses:

  • malloc(0)renvoie toujours un pointeur valide , et
  • free(0) est mauvais.

Ainsi, il s'est assuré que cela artistet d'autres variables avaient toujours une valeur «valide» en eux. Le commentaire dit autant: // these must always point at malloc'd data.

Alok Singhal
la source
10
Le fait qu'il dépende de l'implémentation le rend plus ou moins complètement inutile - c'est l'un des éléments les plus désolés de la norme C, et bon nombre de comités de normalisation (par exemple PJ Plauger) se sont plaints à ce sujet.
10
Je suis d'accord. Si malloc(0)retourné un pointeur valide, alors malloc()retourner NULLsignifie toujours "échec", et 0n'est plus un cas particulier, ce qui est plus cohérent.
Alok Singhal
1
Étant donné que les circonstances de l' mallocéchec d'obtention de la mémoire sont définies par l'implémentation, une implémentation pourrait simplement définir que les allocations de taille 0 sont toujours insatisfiables ( ENOMEM), et que maintenant malloc(0)retourner 0 (avec errno==ENOMEM) est cohérent. :-)
R .. GitHub STOP AIDER ICE
6
Pouvez-vous reallocun pointeur renvoyé par malloc(0)? Pouvez-vous realloc((char*)NULL)?
Braden Best
3
@Braden Best Oui aux deux.
chux - Réintégrer Monica le
11

Le comportement de malloc (0) est spécifique à l'implémentation. La bibliothèque peut renvoyer NULL ou avoir le comportement malloc normal, sans mémoire allouée. Quoi qu'il fasse, il doit être documenté quelque part.

Habituellement, il renvoie un pointeur valide et unique mais qui ne doit PAS être déréférencé. Notez également qu'il PEUT consommer de la mémoire même s'il n'a en fait rien alloué.

Il est possible de réallouer un pointeur malloc (0) non nul.

Avoir un verbatim malloc (0) n'est cependant pas très utile. Il est principalement utilisé lorsqu'une allocation dynamique est de zéro octet et que vous ne vous souciez pas de la valider.

Coincoin
la source
9
malloc()doit conserver quelque part des «informations de gestion» (cette taille du bloc alloué par exemple, et d'autres données auxiliaires). Donc, si malloc(0)ne retourne pas NULL, il utilisera la mémoire pour stocker ces informations, et sinon free()d, constituera une fuite de mémoire.
Alok Singhal du
Les implémentations de Malloc effectuent la tenue de registres qui pourraient ajouter une certaine quantité de données par pointeur renvoyé en plus de la taille demandée.
user7116
1
La mémoire consommée et la mémoire allouée ne signifient pas la même chose. Dans ce cas précis, la plupart des implémentations renverront un pointeur unique. Cela signifie qu'une partie de l'espace d'adressage doit être sacrifiée pour ce pointeur. Selon l'allocateur, cela peut en fait signifier qu'il allouera 1 octet ou plus.
Coincoin
1
La bibliothèque peut faire ce qu'elle veut - eh bien, elle peut soit renvoyer un pointeur unique qu'aucun autre malloc()ne retournera, soit retourner NULL.
Alok Singhal
1
@jldupont: au moins la bibliothèque d'exécution Microsoft C renvoie un pointeur unique pour malloc(0). Cependant, dans la même implémentation de la bibliothèque C standard, realloc(ptr, 0)libère ptret retourne NULL.
Medinoc
5

Il y a une réponse ailleurs sur cette page qui commence "malloc (0) retournera une adresse mémoire valide et dont la plage dépendra du type de pointeur qui est alloué en mémoire". Cette déclaration est incorrecte (je n'ai pas assez de réputation pour commenter cette réponse directement, je ne peux donc pas mettre ce commentaire directement en dessous).

Faire malloc (0) n'allouera pas automatiquement de la mémoire de taille correcte. La fonction malloc ne sait pas dans quoi vous lancez son résultat. La fonction malloc repose uniquement sur le numéro de taille que vous donnez comme argument. Vous devez faire malloc (sizeof (int)) pour obtenir suffisamment de stockage pour contenir un int, par exemple pas 0.

Krellan
la source
4

malloc(0)Cela n'a aucun sens pour moi, à moins que le code ne repose sur un comportement spécifique à l'implémentation. Si le code est destiné à être portable, il doit tenir compte du fait qu'un retour NULL de malloc(0)n'est pas un échec. Alors, pourquoi ne pas simplement attribuer NULL à de artisttoute façon, car c'est un résultat réussi valide, il contient moins de code et que vos programmeurs de maintenance ne prendront pas le temps de le comprendre?

malloc(SOME_CONSTANT_THAT_MIGHT_BE_ZERO)ou malloc(some_variable_which_might_be_zero)peut - être pourraient avoir leurs utilisations, même si encore une fois vous devez faire très attention à ne pas traiter un retour NULL comme un échec si la valeur est 0, mais une taille 0 est censée être OK.

Steve Jessop
la source
3

Il y a beaucoup de demi-vraies réponses ici, alors voici les faits concrets. La page de manuel de malloc()dit:

Si la taille est 0, alors malloc () renvoie NULL ou une valeur de pointeur unique qui peut être transmise ultérieurement avec succès à free ().

Cela signifie qu'il n'y a absolument aucune garantie que le résultat de malloc(0)soit unique ou non NULL. La seule garantie est fournie par la définition de free(), encore une fois, voici ce que dit la page de manuel:

Si ptr est NULL, aucune opération n'est effectuée.

Ainsi, quel que soit le malloc(0)retour, il peut être transmis en toute sécurité free(). Mais un NULLpointeur aussi.

Par conséquent, l'écriture artist = malloc(0); n'est en aucun cas meilleure que l'écriture artist = NULL;

cmaster - réintégrer monica
la source
1
Dommage que l'implémentation ne soit pas autorisée à renvoyer un pointeur non nul et non unique. De cette façon, malloc(0)pourrait renvoyer, disons, 0x1, et free()pourrait avoir une vérification de cas spécial de 0x1 comme il l'a fait pour 0x0.
Todd Lehman
3
@Todd Lehman Une implémentation peut faire ce que vous suggérez. La spécification C ne spécifie pas que le résultat doit être " NULL, ou un pointeur unique". au lieu de "un pointeur nul ou un pointeur vers l'espace alloué". Il n'y a pas d' exigence unique . OTOH, renvoyer une valeur spéciale non unique peut perturber le code qui compte sur des valeurs uniques. Peut-être une question de cas de coin pour SO.
chux - Réintégrer Monica le
manpeut également documenter la forme définie par l'implémentation utilisée dans * nix. Dans ce cas, ce n'est pas le cas, mais ce n'est toujours pas une source canonique pour le général C.
Lundin
@Lundin True. Mais les pages de manuel sont beaucoup plus accessibles que le standard C, et les pages de manuel sur les systèmes GNU / Linux documentent généralement assez bien la ou les normes suivies par l'implémentation. En plus des informations sur les pièces auxquelles adhèrent les normes, si elles diffèrent. J'ai le sentiment qu'ils veulent tous les deux être précis et annoncer chaque
élément
3

Pourquoi tu ne devrais pas faire ça ...

Puisque la valeur de retour de malloc dépend de l'implémentation, vous pouvez récupérer un pointeur NULL ou une autre adresse. Cela peut finir par créer des débordements de mémoire tampon si le code de gestion des erreurs ne vérifie pas à la fois la taille et la valeur renvoyée, ce qui entraîne des problèmes de stabilité (plantages) ou même des problèmes de sécurité pires.

Considérez cet exemple, où un accès supplémentaire à la mémoire via l'adresse renvoyée endommagera le tas si la taille du tas est égale à zéro et l'implémentation renvoie une valeur non NULL.

size_t size;

/* Initialize size, possibly by user-controlled input */

int *list = (int *)malloc(size);
if (list == NULL) {
  /* Handle allocation error */
}
else {
  /* Continue processing list */
}

Voir cette page de codage sécurisé de CERT Coding Standards où j'ai pris l'exemple ci-dessus pour plus de lecture.

Auselen
la source
Le lien a été déplacé: wiki.sei.cmu.edu/confluence/display/c
...
2

Certes, je n'ai jamais vu cela auparavant, c'est la première fois que je vois cette syntaxe, pourrait-on dire, un cas classique de surpuissance des fonctions. En conjonction avec la réponse de Reed, je voudrais souligner qu'il existe une chose similaire, qui ressemble à une fonction surchargée realloc:

  • foo est non NULL et la taille est égale à zéro, realloc(foo, size);. Lorsque vous passez un pointeur non NULL et une taille de zéro à realloc, realloc se comporte comme si vous aviez appelé free (…)
  • foo est NULL et la taille est différente de zéro et supérieure à 1 realloc(foo, size);,. Lorsque vous passez un pointeur NULL et que la taille est différente de zéro, realloc se comporte comme si vous aviez appelé malloc (…)

J'espère que cela aide, Meilleures salutations, Tom.

t0mm13b
la source
1

Pour répondre réellement à la question posée: il n'y a aucune raison de faire ça

Paolo
la source
0

malloc (0) retournera NULL ou un pointeur valide qui peut être correctement passé à free. Et même s'il semble que la mémoire vers laquelle elle pointe est inutile ou ne peut pas être écrite ou lue, ce n'est pas toujours vrai. :)

int *i = malloc(0);
*i = 100;
printf("%d", *i);

On s'attend à un défaut de segmentation ici, mais étonnamment, cela imprime 100! C'est parce que malloc demande en fait une énorme quantité de mémoire lorsque nous appelons malloc pour la première fois. Chaque appel à malloc après cela utilise la mémoire de ce gros morceau. Ce n'est qu'après que cet énorme morceau est terminé, une nouvelle mémoire est demandée.

Utilisation de malloc (0): si vous êtes dans une situation où vous voulez que les appels de malloc suivants soient plus rapides, appeler malloc (0) devrait le faire pour vous (sauf pour les cas extrêmes).

Sagar Bhosale
la source
1
L'écriture dans *ipeut ne pas planter dans votre cas, mais c'est néanmoins un comportement non défini. Attention aux démons nasillards!
John Dvorak
1
Oui. C'est vrai. C'est spécifique à la mise en œuvre. Je l'ai vérifié sur MaxOS X et une distribution Linux. Je ne l'ai pas essayé sur d'autres plateformes. Cela dit, le concept que j'ai décrit a été décrit dans le livre "Le langage de programmation C" de Brain Kernighan et Dennis Ritchie.
Sagar Bhosale
Je sais: commentaire très tardif sur cette question. Mais il y a parfois une utilisation pour malloc(0)qui n'est pas mentionnée. Sur les implémentations où il renvoie une valeur non NULL, en particulier dans une construction DEBUG, il alloue probablement PLUS que ce que vous avez demandé et vous donne le pointeur juste après son en-tête interne. Cela vous permet d'avoir une idée de l'utilisation réelle de la mémoire si vous obtenez cela avant et après une série d'allocations. par exemple: void* before = malloc(0); ... void* after = malloc(0); long long total = after - before;ou certains tels.
Jesse Chisholm
J'ai lu "Le langage de programmation C" de Brain Kernighan et Dennis Ritchie et je ne me souviens pas qu'il en dise quoi que ce soit malloc(0). Pouvez-vous nous dire à quel chapitre vous parlez également? Fournir un devis exact serait également bien.
Андрей Беньковский
0

Sous Windows:

  • void *p = malloc(0);allouera un tampon de longueur nulle sur le tas local. Le pointeur renvoyé est un pointeur de tas valide.
  • mallocappelle finalement en HeapAllocutilisant le tas d'exécution C par défaut qui appelle ensuite RtlAllocateHeap, etc.
  • free(p);utilise HeapFreepour libérer le tampon de longueur 0 sur le tas. Ne pas le libérer entraînerait une fuite de mémoire.
sam msft
la source
0

C'est en fait très utile, et (évidemment à mon humble avis), le comportement autorisé de renvoyer un pointeur NULL est cassé. Un pointeur dynamique est utile non seulement pour ce sur quoi il pointe, mais aussi pour le fait que son adresse est unique. Renvoyer NULL supprime cette deuxième propriété. Tous les programmes intégrés de mallocs I (assez fréquemment en fait) ont ce comportement.

Scott Franco
la source
-2

Voici l'analyse après exécution avec l'outil de vérification de la mémoire valgrind.

==16740== Command: ./malloc0
==16740==
p1 = 0x5204040
==16740==
==16740== HEAP SUMMARY:
==16740==     in use at exit: 0 bytes in 0 blocks
==16740==   total heap usage: 2 allocs, 2 frees, 1,024 bytes allocated
==16740==
==16740== All heap blocks were freed -- no leaks are possible

et voici mon exemple de code:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
   //int i;
   char *p1;

   p1 = (char *)malloc(0);
   printf("p1 = %p\n", p1);

   free(p1);

   return 0;

}

Par défaut, 1024 octets sont alloués. Si j'augmente la taille de malloc, les octets alloués augmenteront de 1025 et ainsi de suite.

Shubhesh Swain
la source
-3

D'après la réponse de Reed Copsey et la page de manuel de malloc, j'ai écrit quelques exemples à tester. Et j'ai découvert que malloc (0) lui donnera toujours une valeur unique. Voir mon exemple:

char *ptr;
if( (ptr = (char *) malloc(0)) == NULL )
    puts("Got a null pointer");
else
    puts("Got a valid pointer");

La sortie sera "Vous avez un pointeur valide", ce qui signifie qu'il ptrn'est pas nul.

Neal
la source
-6

malloc(0)renverra une adresse mémoire valide et dont la plage dépendra du type de pointeur auquel la mémoire est allouée. Vous pouvez également attribuer des valeurs à la zone de mémoire, mais cela doit être dans la plage avec le type de pointeur utilisé. Vous pouvez également libérer la mémoire allouée. Je vais expliquer cela avec un exemple:

int *p=NULL;
p=(int *)malloc(0);
free(p);

Le code ci-dessus fonctionnera correctement dans un gcccompilateur sur une machine Linux. Si vous avez un compilateur 32 bits, vous pouvez fournir des valeurs dans la plage entière, c'est-à-dire -2147483648 à 2147483647. Il en va de même pour les caractères. Veuillez noter que si le type de pointeur déclaré est modifié, la plage de valeurs changera quel que soit le malloctypage, c'est-à-dire

unsigned char *p=NULL;
p =(char *)malloc(0);
free(p);

p prendra une valeur de 0 à 255 de char car il est déclaré un int non signé.

Suryakant Tiwari
la source
5
Krellan a raison de souligner que cette réponse est fausse: malloc()ne sait rien de la distribution (qui est en fait entièrement superfluente en C). Le déréférencement de la valeur de retour de malloc(0)provoquera un comportement indéfini.
cmaster - réintégrer monica le
-6

Juste pour corriger une fausse impression ici:

artist = (char *) malloc(0);ne reviendra jamais NULL; ce n'est pas la même chose que artist = NULL;. Écrivez un programme simple et comparez artistavec NULL. if (artist == NULL)est faux et if (artist)vrai.

ALFRED OKORONKWO
la source