web-dev-qa-db-fra.com

Si `malloc (0)` renvoie un pointeur non nul, puis-je le passer à `free`?

J'ai relu les discussions sur le comportement de malloc lorsque vous demandez un bloc de taille nulle.

Je comprends que le comportement de malloc(0) est défini par l'implémentation, et il est censé renvoyer un pointeur nul, o un pointeur non nul auquel je ne suis néanmoins pas censé accéder . (Ce qui est logique, car il n'y a aucune garantie qu'il pointe vers une mémoire utilisable.)

Cependant, si un get un tel pointeur non null non accessible, suis-je autorisé à le passer à free de la manière habituelle? Ou est-ce illégal, puisque le pointeur que j'obtiens de malloc(0) peut ne pas pointer vers un bloc de mémoire alloué réel?

Concrètement, le code suivant a-t-il un comportement bien défini:

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

int main() {
    int* x = (int*) malloc(0);
    if (x == NULL) {
        printf("Got NULL\n");
        return 0;
    } else {
        printf("Got nonnull %p\n", x);
    }
    free(x); // is calling `free` here okay?
}
43
pnkfelix

La norme C99 (en fait WG14/N1124. Projet de comité - 6 mai 2005. ISO/CEI 9899: TC2) dit à propos de malloc():

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é ne doit pas être utilisé pour accéder à un objet

et à propos de free():

Sinon, si l'argument ne correspond pas à un pointeur précédemment renvoyé par la fonction calloc, malloc ou realloc, ou si l'espace a été désalloué par un appel à free ou realloc, le comportement n'est pas défini.

IEEE Std 1003.1-2008 (POSIX), 2016 Edition dit à propos de free():

La fonction free () provoquera la désallocation de l'espace pointé par ptr; c'est-à-dire mis à disposition pour une allocation ultérieure. Si ptr est un pointeur nul, aucune action ne doit se produire. Sinon, si l'argument ne correspond pas à un pointeur renvoyé précédemment par une fonction dans POSIX.1-2008 qui alloue de la mémoire comme par malloc (), ou si l'espace a été désalloué par un appel à free () ou realloc (), le comportement n'est pas défini.

Donc, quel que soit *alloc() renvoie, vous pouvez passer à free().

Comme pour les implémentations actuelles de malloc():

FreeBSD utilise le jemalloc fourni qui ne

void *
je_malloc(size_t size)
{
    void *ret;
    size_t usize JEMALLOC_CC_SILENCE_INIT(0);

    if (size == 0)
        size = 1;
    [...]

Alors que libmalloc d'Apple ne

void *
szone_memalign(szone_t *szone, size_t alignment, size_t size)
{
    if (size == 0) {
        size = 1; // Ensures we'll return an aligned free()-able pointer
    [...]

Le GLIBC modifie également la taille demandée; il utilise un appel à cette macro avec la taille demandée en octets comme paramètre pour aligner la taille sur une certaine frontière ou simplement la taille d'allocation minimale:

#define request2size(req)                                       \
    (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE)  ?         \
    MINSIZE :                                                   \
    ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
41
apriori

Oui, en fait, vous devez le faire pour éviter une fuite de mémoire probable.

Le système malloc renvoie généralement un bloc de contrôle caché dans l'espace immédiatement avant le pointeur, avec des informations telles que la taille de l'allocation. Si la taille d'allocation est nulle, ce bloc existe toujours et occupe la mémoire, si malloc retourne non nul.

24
Malcolm McLean