web-dev-qa-db-fra.com

malloc pour struct et pointeur en C

Supposons que je veuille définir une structure représentant la longueur du vecteur et ses valeurs comme suit:

struct Vector{
    double* x;
    int n;
};

Supposons maintenant que je veuille définir un vecteur y et y allouer de la mémoire.

struct Vector *y = (struct Vector*)malloc(sizeof(struct Vector));

Ma recherche sur Internet montre que je devrais allouer la mémoire pour x séparément.

y->x = (double*)malloc(10*sizeof(double));

Mais, il semble que j'alloue la mémoire pour y-> x deux fois, l'une en allouant de la mémoire pour y et l'autre en allouant de la mémoire pour y-> x, et cela me semble un gaspillage de mémoire. Il est très apprécié que vous me disiez ce que fait le compilateur et quelle serait la bonne façon d’initialiser y et y-> x.

Merci d'avance.

66
Pouya

Non, vous pas allouez de la mémoire pour y->x deux fois.

Au lieu de cela, vous allouez de la mémoire à la structure (qui inclut un pointeur) plus quelque chose que ce pointeur doit désigner.

Pense-y de cette façon:

         1          2
        +-----+    +------+
y------>|  x------>|  *x  |
        |  n  |    +------+
        +-----+

Donc, vous avez réellement besoin des deux allocations (1 et 2) pour tout stocker.

De plus, votre type doit être struct Vector *y puisqu'il s'agit d'un pointeur et que vous ne devez jamais transtyper la valeur de retour de malloc en C car il peut masquer certains problèmes que vous ne voulez pas masquer - C est parfaitement capable de convertir implicitement la valeur de retour void* en un autre pointeur.

Et, bien sûr, vous souhaiterez probablement encapsuler la création de ces vecteurs pour en faciliter la gestion, comme avec:

struct Vector {
    double *data;    // no place for x and n in readable code :-)
    size_t size;
};

struct Vector *newVector (size_t sz) {
    // Try to allocate vector structure.

    struct Vector *retVal = malloc (sizeof (struct Vector));
    if (retVal == NULL)
        return NULL;

    // Try to allocate vector data, free structure if fail.

    retVal->data = malloc (sz * sizeof (double));
    if (retVal->data == NULL) {
        free (retVal);
        return NULL;
    }

    // Set size and return.

    retVal->size = sz;
    return retVal;
}

void delVector (struct Vector *vector) {
    // Can safely assume vector is NULL or fully built.

    if (vector != NULL) {
        free (vector->data);
        free (vector);
    }
}

En encapsulant la création ainsi, vous vous assurez que les vecteurs sont entièrement construits ou ne le sont pas du tout - il n’ya aucune chance qu’ils soient à moitié construits. Cela vous permet également de modifier totalement les structures de données sous-jacentes à l’avenir sans affecter les clients (par exemple, si vous souhaitez leur faire des tableaux épars pour échanger de la place pour plus de rapidité).

128
paxdiablo

La première fois, vous allouez de la mémoire pour Vector, ce qui signifie les variables x, n.

Cependant xne pointe pas encore quelque chose d'utile.

C’est la raison pour laquelle ne deuxième allocation est également nécessaire.

5
Karthik T

Peu de points

struct Vector y = (struct Vector*)malloc(sizeof(struct Vector)); est faux

ce devrait être struct Vector *y = (struct Vector*)malloc(sizeof(struct Vector)); puisque y maintient le pointeur sur struct Vector.

1st malloc() n'alloue que suffisamment de mémoire pour contenir la structure Vector (pointeur double + int)

2nd malloc() alloue effectivement de la mémoire pour contenir 10 doubles.

3
rajneesh

En principe, vous le faites déjà correctement. Pour ce que vous voulez, vous avez besoin de deux malloc()s.

Juste quelques commentaires:

struct Vector y = (struct Vector*)malloc(sizeof(struct Vector));
y->x = (double*)malloc(10*sizeof(double));

devrait être

struct Vector *y = malloc(sizeof *y); /* Note the pointer */
y->x = calloc(10, sizeof *y->x);

Dans la première ligne, vous allouez de la mémoire pour un objet Vector. malloc() renvoie un pointeur sur la mémoire allouée, donc y doit être un pointeur Vector. Sur la deuxième ligne, vous allouez de la mémoire pour un tableau de 10 doublons.

En C, vous n’avez pas besoin de transtypages explicites, et écrire sizeof *y à la place de sizeof(struct Vector) est préférable pour la sécurité des types, et permet en outre d’économiser sur la frappe.

Vous pouvez réorganiser votre structure et faire un seul malloc() comme ceci:

struct Vector{    
    int n;
    double x[];
};
struct Vector *y = malloc(sizeof *y + 10 * sizeof(double));
1
Wernsey

Vous pouvez réellement le faire dans un seul malloc en allouant simultanément le vecteur et le tableau. Par exemple:

struct Vector y = (struct Vector*)malloc(sizeof(struct Vector) + 10*sizeof(double));
y->x = (double*)((char*)y + sizeof(struct Vector));
y->n = 10;

Ceci alloue le vecteur 'y', puis fait pointer y-> x vers les données allouées supplémentaires immédiatement après la structure du vecteur (mais dans le même bloc de mémoire).

Si le redimensionnement du vecteur est requis, vous devez le faire avec les deux affectations recommandées. Le tableau interne y-> x pourrait alors être redimensionné tout en conservant la structure vectorielle 'y'.

1
PQuinn

Lorsque vous allouez de la mémoire pour struct Vector, vous allouez simplement de la mémoire pour le pointeur x, c’est-à-dire pour l’espace, où sa valeur, qui contient l’adresse, sera placée. Ainsi, vous n'allouez pas de la mémoire pour le bloc sur lequel y.x fera référence.

0
Andremoniy

D'abord, malloc alloue de la mémoire pour struct, y compris de la mémoire pour x (pointeur sur double). Le second malloc alloue de la mémoire pour une valeur double à laquelle x pointe.

0
oleg_g