Que renvoie malloc(0)
? La réponse serait-elle la même pour realloc(malloc(0),0)
?
#include<stdio.h>
#include<malloc.h>
int main()
{
printf("%p\n", malloc(0));
printf("%p\n", realloc(malloc(0), 0));
return 0;
}
Sortie de linux gcc:
manav@manav-workstation:~$ gcc -Wall mal.c
manav@manav-workstation:~$ ./a.out
0x9363008
(nil)
manav@manav-workstation:~$
La sortie continue de changer à chaque fois pour malloc(0)
. Est-ce une réponse standard? Et pourquoi serait-on intéressé à obtenir un tel pointeur, autre que la recherche universitaire?
MODIFIER:
Si malloc(0)
renvoie un pointeur factice, alors comment fonctionne ce qui suit:
int main()
{
void *ptr = malloc(0);
printf("%p\n", realloc(ptr, 1024));
return 0;
}
MODIFIER:
Le code suivant affiche "possible" pour chaque itération. Pourquoi ne devrait-il pas échouer?
#include<stdio.h>
#include<malloc.h>
int main()
{
int i;
void *ptr;
printf("Testing using BRUTE FORCE\n");
for (i=0; i<65000; i++)
{
ptr = malloc(0);
if (ptr == realloc(ptr, 1024))
printf("Iteration %d: possible\n", i);
else
{
printf("Failed for iteration %d\n", i);
break;
}
}
return 0;
}
D'autres ont répondu au fonctionnement de malloc(0)
. Je vais répondre à l'une des questions que vous avez posées qui n'ont pas encore reçu de réponse (je pense). La question concerne realloc(malloc(0), 0)
:
Que renvoie
malloc(0)
? La réponse serait-elle la même pourrealloc(malloc(0),0)
?
La norme le dit à propos de realloc(ptr, size)
:
ptr
est NULL
, il se comporte comme malloc(size)
,ptr
n'est pas NULL
), il désalloue l'ancien pointeur d'objet vers par ptr
et renvoie un pointeur vers un nouveau tampon alloué. Mais si size
vaut 0, C89 dit que l'effet est équivalent à free(ptr)
. Fait intéressant, je ne trouve pas cette déclaration dans le projet C99 (n1256 ou n1336). En C89, la seule valeur raisonnable à renvoyer dans ce cas serait NULL
.Il y a donc deux cas:
malloc(0)
renvoie NULL
sur une implémentation. Votre appel realloc()
est alors équivalent à realloc(NULL, 0)
. Cela équivaut à malloc(0)
d'en haut (et c'est NULL
dans ce cas).malloc(0)
renvoie non -NULL
. Ensuite, l'appel équivaut à free(malloc(0))
. Dans ce cas, malloc(0)
et realloc(malloc(0), 0)
sont pas équivalents.Notez qu'il y a un cas intéressant ici: dans le second cas, lorsque malloc(0)
retourne non -NULL
en cas de succès, il peut toujours retourner NULL
pour indiquer l'échec. Cela se traduira par un appel comme: realloc(NULL, 0)
, qui serait équivalent à malloc(0)
, qui peut ou non retourner NULL
.
Je ne sais pas si l'omission dans C99 est une erreur ou si cela signifie que dans C99, realloc(ptr, 0)
pour nonNULL
ptr
n'est pas équivalent à free(ptr)
. Je viens d'essayer ceci avec gcc -std=c99
, Et ce qui précède est équivalent à free(ptr)
.
Edit: Je pense que je comprends votre confusion:
Regardons un extrait de votre exemple de code:
ptr = malloc(0);
if (ptr == realloc(ptr, 1024))
Ce qui précède n'est pas le même que malloc(0) == realloc(malloc(0), 1024)
. Dans le second, l'appel malloc()
est effectué deux fois, tandis que dans le premier, vous passez un pointeur précédemment alloué à realloc()
.
Analysons d'abord le premier code. En supposant que malloc(0)
ne renvoie pas NULL
en cas de succès, ptr
a une valeur valide. Lorsque vous faites realloc(ptr, 1024)
, realloc()
vous donne essentiellement un nouveau tampon qui a la taille 1024, et le ptr
devient invalide. Une implémentation conforme peut renvoyer la même adresse que celle déjà dans ptr
. Ainsi, votre condition if
peut retourner true. (Notez cependant que regarder la valeur de ptr
après realloc(ptr, 1024)
peut être un comportement non défini.)
Maintenant, la question que vous posez: malloc(0) == realloc(malloc(0), 1024)
. Dans ce cas, supposons que la malloc(0)
du LHS et du RHS renvoie non -NULL
. Ensuite, ils sont garantis d'être différents. De plus, la valeur renvoyée par malloc()
sur le LHS n'a pas encore été free()
d, donc toute autre malloc()
, calloc()
ou realloc()
peut ne pas retourner cette valeur. Cela signifie que si vous avez écrit votre condition comme:
if (malloc(0) == realloc(malloc(0), 1024)
puts("possible");
vous ne verrez pas possible
sur la sortie (à moins que malloc()
et realloc()
échouent et renvoient NULL
).
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
void *p1;
void *p2;
p1 = malloc(0);
p2 = realloc(p1, 1024);
if (p1 == p2)
puts("possible, OK");
/* Ignore the memory leaks */
if (malloc(0) == realloc(malloc(0), 1024))
puts("shouldn't happen, something is wrong");
return 0;
}
Sous OS X, mon code n'a rien sorti lorsque je l'ai exécuté. Sous Linux, il imprime possible, OK
.
malloc(0)
is Implementation Defined en ce qui concerne C99.
De C99 [Section 7.20.3]
L'ordre et la contiguïté du stockage alloués par appels successifs aux fonctions calloc, malloc et realloc ne sont pas précisés. Le pointeur renvoyé si l'allocation réussit est correctement aligné de sorte qu'il peut être affecté à un pointeur sur n'importe quel type d'objet, puis utilisé pour accéder à un tel objet ou à un tableau de ces objets dans l'espace alloué (jusqu'à ce que l'espace soit explicitement désalloué) . La durée de vie d'un objet alloué s'étend de l'allocation jusqu'à la désallocation. Chacune de ces allocations donnera un pointeur vers un objet disjoint de tout autre objet. Le pointeur a renvoyé des points au début (adresse d'octet le plus bas) de l'espace alloué. Si l'espace ne peut pas être alloué, un pointeur nul est renvoyé. 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é doit ne pas être utilisé pour accéder à un objet.
En C89, malloc (0) dépend de l'implémentation - je ne sais pas si C99 a corrigé cela ou non. En C++, en utilisant:
char * p = new char[0];
est bien défini - vous obtenez un pointeur valide et non nul. Bien sûr, vous ne pouvez pas utiliser le pointeur pour accéder à ce qu'il pointe sans invoquer un comportement non défini.
Quant à savoir pourquoi cela existe, il est pratique pour certains algorithmes et signifie que vous n'avez pas besoin de jeter votre code avec des tests pour des valeurs nulles.
Norme C99
Si l'espace ne peut pas être alloué, un pointeur nul est retourné. 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 .
comp.lang.c FAQ a ce qui suit pour dire:
La norme ANSI/ISO dit qu'elle peut faire l'une ou l'autre; le comportement est défini par l'implémentation (voir la question 11.33). Le code portable doit soit prendre soin de ne pas appeler malloc (0), soit être préparé à la possibilité d'un retour nul.
Il est donc préférable d'éviter d'utiliser malloc(0)
.
Voir C99, section 7.20.3:
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.
Ceci est valable pour les trois fonctions d'allocation (c'est-à-dire calloc()
, malloc()
et realloc()
).
Un point dont personne ne voulait parler pour l'instant, dans votre premier programme, c'est que realloc
de longueur 0 est la même chose que free
.
à partir de la page de manuel Solaris:
La fonction
realloc()
modifie la taille du bloc pointé parptr
ensize
octets et renvoie un pointeur sur le bloc (éventuellement déplacé). Le contenu sera inchangé jusqu'à la moindre des nouvelles et anciennes tailles. Siptr
estNULL
,realloc()
se comporte commemalloc()
pour la taille spécifiée. Sisize
est0
etptr
n'est pas un pointeur nul, l'espace pointé est rendu disponible pour une allocation ultérieure par l'application, bien qu'il ne soit pas renvoyé au système. La mémoire n'est renvoyée au système qu'à la fin de l'application.
Si on ne sait pas que ça peut être une source de mauvaise surprise (ça m'est arrivé).
Je pense que cela dépend. J'ai vérifié les sources de Visual Studio 2005 et l'ai vu dans la fonction _heap_alloc:
if (size == 0)
size = 1;
Je pense que dans de nombreux cas, vous pouvez vouloir un pointeur valide, même lorsque vous demandez zéro octet. En effet, ce comportement cohérent facilite la vérification de vos pointeurs car: si vous avez un pointeur non NULL, c'est OK; si vous avez un pointeur NULL, vous avez probablement un problème. C'est pourquoi je pense que la plupart des implémentations renverront un pointeur valide, même en demandant zéro octet.
Si malloc (0) renvoie un pointeur factice, alors comment fonctionne le suivant:
void *ptr = malloc(0);
printf("%p\n", realloc(ptr, 1024));
Je ne sais pas ce que vous entendez par "pointeur factice". Si malloc(0)
retourne non NULL, alors ptr
est un pointeur valide vers un bloc mémoire de taille zéro. L'implémentation malloc
enregistre ces informations d'une manière spécifique à l'implémentation. realloc
connaît la manière (spécifique à l'implémentation) de savoir que ptr
pointe vers un bloc mémoire de taille zéro.
(La façon dont malloc
/realloc
/free
est spécifique à l'implémentation. Une possibilité consiste à allouer 4 octets de plus que ce qui est demandé et à stocker la taille juste avant le bloc de mémoire. ce cas, ((int *)ptr)[-1]
donnerait la taille du bloc de mémoire, qui est 0
. Vous ne devriez jamais faire cela à partir de votre code, c'est uniquement pour une utilisation par realloc
et free
).