Question

J'essaie de créer une fonction qui prend un tableau en argument, lui ajoute des valeurs (en augmentant sa taille si nécessaire) et renvoie le nombre d'éléments. Jusqu'à présent j'ai:

int main(int argc, char** argv) {
    int mSize = 10;
    ent a[mSize];
    int n;
    n = addValues(a,mSize);

    for(i=0;i<n;i++) {
       //Print values from a
    }
}

int addValues(ent *a, int mSize) {
    int size = mSize;

    i = 0;

    while(....) { //Loop to add items to array
        if(i>=size-1) { 
            size = size*2;
            a = realloc(a, (size)*sizeof(ent));
        }
        //Add to array
        i++;
    }
    return i;
}

Ceci fonctionne si mSize est suffisamment grand pour contenir tous les éléments potentiels du tableau, mais s'il nécessite un redimensionnement, un défaut de segmentation se produit.

J'ai aussi essayé:

int main(int argc, char** argv) {
    ...
    ent *a;
    ...
}

int addValues(ent *a, int mSize) {
    ...
    a = calloc(1, sizeof(ent);
    //usual loop
    ...
}

En vain.

Je suppose que c'est parce que lorsque j'appelle realloc, la copie de "a" est dirigée ailleurs - comment est-il possible de modifier cela de sorte que "a" pointe toujours vers le même emplacement?

Est-ce que j'y vais correctement? Existe-t-il de meilleurs moyens de gérer les structures dynamiques en C? Devrais-je mettre en place une liste chaînée pour y faire face?

Était-ce utile?

La solution

Le problème principal ici est que vous essayez d'utiliser realloc avec un tableau alloué par pile. Vous avez:

ent a[mSize];

C'est l'allocation automatique sur la pile. Si vous souhaitez utiliser realloc () sur cela plus tard, vous devez créer le tableau sur le tas en utilisant malloc (), comme ceci:

ent *a = (ent*)malloc(mSize * sizeof(ent));

Pour que la bibliothèque malloc (et donc realloc (), etc.) connaisse votre tableau. Cela pourrait vous confondre avec les tableaux C99 de longueur variable avec les véritables tableaux dynamiques , assurez-vous donc de bien comprendre la différence avant d'essayer de résoudre ce problème.

En réalité, si vous écrivez des tableaux dynamiques en C, vous devriez essayer d’utiliser la conception POO-ish pour encapsuler des informations sur vos tableaux et les masquer à l’utilisateur. Vous souhaitez consolider des informations (pointeur et taille, par exemple) sur votre tableau en une structure et des opérations (allocation, ajout d'éléments, suppression d'éléments, libération, etc.) dans des fonctions spéciales fonctionnant avec votre structure. Donc, vous pourriez avoir:

typedef struct dynarray {
   elt *data;
   int size;
} dynarray;

Et vous pourriez définir certaines fonctions pour travailler avec dynarrays:

// malloc a dynarray and its data and returns a pointer to the dynarray    
dynarray *dynarray_create();     

// add an element to dynarray and adjust its size if necessary
void dynarray_add_elt(dynarray *arr, elt value);

// return a particular element in the dynarray
elt dynarray_get_elt(dynarray *arr, int index);

// free the dynarray and its data.
void dynarray_free(dynarray *arr);

De cette façon, l’utilisateur n’a pas à se rappeler exactement comment allouer les objets ou quelle est la taille actuelle du tableau. J'espère que cela vous aidera à démarrer.

Autres conseils

Essayez de le retravailler afin qu'un pointeur vers un pointeur vers le tableau soit passé, c'est-à-dire ent ** a . Ensuite, vous pourrez mettre à jour l'appelant sur le nouvel emplacement du tableau.

C’est une bonne raison d’utiliser la POO. oui, vous pouvez faire de la programmation orientée objet sur C, et même si cela est fait correctement, cela a l'air bien.

dans ce cas simple, vous n'avez pas besoin d'héritage ni de polymorphisme, mais des concepts d'encapsulation et de méthodes:

  • définit une structure avec une longueur et un pointeur de données. peut-être une taille d'élément.
  • écrit les fonctions de lecture / définition qui agissent sur les pointeurs de cette structure.
  • la fonction 'Grow' modifie le pointeur de données dans la structure, mais tout pointeur de structure reste valide.

Si vous avez modifié la déclaration de variable dans main pour qu'elle soit

ent *a = NULL;

le code fonctionnerait plus comme vous l’imaginiez en ne libérant pas un tableau alloué par pile. Définir a sur NULL fonctionne parce que realloc traite cela comme si l'utilisateur appelait malloc (taille). Gardez à l'esprit qu'avec cette modification, le prototype à ajouterValeur doit être remplacé par

.
int addValues(ent **a, int mSize)

et que le code doit traiter le cas d'échec de realloc. Par exemple

while(....) { //Loop to add items to array
    tmp = realloc(*a, size*sizeof(ent));
    if (tmp) {
        *a = tmp;
    } else {
        // allocation failed. either free *a or keep *a and
        // return an error
    }
    //Add to array
    i++;
}

Je m'attendrais à ce que la plupart des implémentations de realloc allouent en interne deux fois plus de mémoire si le tampon actuel doit être redimensionné, ce qui rendra le code original

size = size * 2;

inutile.

Vous passez le pointeur de tableau par valeur. Cela signifie:

int main(int argc, char** argv) {
    ...
    ent *a; // This...
    ...
}

int addValues(ent *a, int mSize) {
    ...
    a = calloc(1, sizeof(ent); // ...is not the same as this
    //usual loop
    ...
}

donc changer la valeur de a dans la fonction addValues ?? ne change pas la valeur de a in main. Pour modifier la valeur d'un élément principal, vous devez lui attribuer la référence addValues ??. Pour le moment, la valeur de a est copiée et transmise à addValues ??. Pour passer une référence à un usage:

int addValues (int **a, int mSize)

et appelez-le comme suit:

int main(int argc, char** argv) {
    ...
    ent *a; // This...
    ...
    addValues (&a, mSize);
}

Dans addValues ??, accédez aux éléments de cette manière:

(*a)[element]

et réaffecter le tableau comme suit:

(*a) = calloc (...);

Xahtep explique comment votre appelant peut gérer le fait que realloc () peut déplacer le tableau vers un nouvel emplacement. Tant que vous faites cela, ça devrait aller.

realloc () peut coûter cher si vous commencez à travailler avec de grands tableaux. C'est alors qu'il est temps de penser à utiliser d'autres structures de données - une liste chaînée, un arbre binaire, etc.

Comme indiqué, vous devez transmettre le pointeur à pointeur pour mettre à jour la valeur du pointeur.
Mais je suggérerais de revoir la conception et d’éviter cette technique; dans la plupart des cas, elle peut et doit être évitée. Sans savoir ce que vous essayez exactement de réaliser, il est difficile de suggérer une conception alternative, mais je suis sûr à 99% que c'est faisable autrement. Et comme Javier sad - pensez aux objets et vous obtiendrez toujours un meilleur code.

Êtes-vous vraiment obligé d'utiliser C? Ce serait une excellente application de "std :: vector" de C ++, qui est précisément un tableau de taille dynamique (redimensionnable facilement avec un seul appel, vous n'avez pas à écrire et à déboguer vous-même).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top