web-dev-qa-db-fra.com

Comment créer un tableau de structures de taille dynamique?

Je sais comment créer un tableau de structures mais avec une taille prédéfinie. Cependant, existe-t-il un moyen de créer un tableau dynamique de structures telles que le tableau pourrait s'agrandir?

Par exemple:

    typedef struct
    {
        char *str;
    } words;

    main()
    {
        words x[100]; // I do not want to use this, I want to dynamic increase the size of the array as data comes in.
    }

Est-ce possible?


J'ai recherché ceci: words* array = (words*)malloc(sizeof(words) * 100);

Je veux me débarrasser des 100 et stocker les données au fur et à mesure. Ainsi, si 76 champs de données sont entrés, je veux en stocker 76 et non pas 100. Je suppose que je ne sais pas combien de données vont arriver dans mon programme. Dans la structure que j'ai définie ci-dessus, je pourrais créer le premier "index" en tant que:

    words* array = (words*)malloc(sizeof(words));

Cependant, je veux ajouter dynamiquement des éléments au tableau après. J'espère avoir décrit le problème assez clairement. Le défi majeur consiste à ajouter dynamiquement un deuxième domaine, du moins le défi du moment.


J'ai cependant fait un petit progrès:

    typedef struct {
        char *str;
    } words;

    // Allocate first string.
    words x = (words) malloc(sizeof(words));
    x[0].str = "john";

    // Allocate second string.
    x=(words*) realloc(x, sizeof(words));
    x[1].FirstName = "bob";

    // printf second string.
    printf("%s", x[1].str); --> This is working, it's printing out bob.

    free(x); // Free up memory.

    printf("%s", x[1].str); --> Not working since its still printing out BOB even though I freed up memory. What is wrong?

J'ai fait une erreur de vérification et voici ce que j'ai trouvé. Si, après avoir libéré de la mémoire pour x, j'ajoute ce qui suit:

    x=NULL;

alors, si j'essaie d'imprimer x, j'obtiens une erreur, ce que je veux. Alors, est-ce que la fonction free ne fonctionne pas, du moins sur mon compilateur? J'utilise DevC ??


Merci, je comprends maintenant à cause de:

FirstName est un pointeur sur un tableau de caractères qui n'est pas alloué par le malloc, seul le pointeur est alloué et après que vous appelez free, il n'efface pas la mémoire, il le marque simplement comme étant disponible sur le tas écrit plus tard. - Matt Smith

Mettre à jour

J'essaie de modulariser et de mettre la création de mon tableau de struct dans une fonction mais rien ne semble fonctionner. J'essaie quelque chose de très simple et je ne sais pas quoi faire d'autre. Cela va dans le même sens qu'auparavant, juste une autre fonction, loaddata qui charge les données et en dehors de la méthode dont j'ai besoin pour imprimer. Comment puis-je le faire fonctionner? Mon code est le suivant:

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

    typedef struct
    {
        char *str1;
        char *str2;
    } words;

    void LoadData(words *, int *);

    main()
    {
        words *x;
        int num;

        LoadData(&x, &num);

        printf("%s %s", x[0].str1, x[0].str2);
        printf("%s %s", x[1].str1, x[1].str2);

        getch();
    }//

    void LoadData(words *x, int * num)
    {
        x = (words*) malloc(sizeof(words));

        x[0].str1 = "johnnie\0";
        x[0].str2 = "krapson\0";

        x = (words*) realloc(x, sizeof(words)*2);
        x[1].str1 = "bob\0";
        x[1].str2 = "marley\0";

        *num=*num+1;
    }//

Ce code de test simple plante et je ne sais pas pourquoi. Où est le bug?

49
D. Rattansingh

Vous avez étiqueté cela en tant que C++ et C.

Si vous utilisez C++, les choses sont beaucoup plus faciles. La bibliothèque de modèles standard comporte un modèle appelé vector qui vous permet de créer de manière dynamique une liste d'objets.

#include <stdio.h>
#include <vector>

typedef std::vector<char*> words;

int main(int argc, char** argv) {

        words myWords;

        myWords.Push_back("Hello");
        myWords.Push_back("World");

        words::iterator iter;
        for (iter = myWords.begin(); iter != myWords.end(); ++iter) {
                printf("%s ", *iter);
        }

        return 0;
}

Si vous utilisez le C, les choses sont beaucoup plus difficiles, oui, malloc, realloc et free sont les outils pour vous aider. Vous voudrez peut-être envisager d'utiliser une structure de données de liste chaînée. Celles-ci sont généralement plus faciles à développer, mais ne facilitent pas l'accès aléatoire aussi facilement.

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

typedef struct s_words {
        char* str;
        struct s_words* next;
} words;

words* create_words(char* Word) {
        words* newWords = malloc(sizeof(words));
        if (NULL != newWords){
                newWords->str = Word;
                newWords->next = NULL;
        }
        return newWords;
}

void delete_words(words* oldWords) {
        if (NULL != oldWords->next) {
                delete_words(oldWords->next);
        }
        free(oldWords);
}

words* add_Word(words* wordList, char* Word) {
        words* newWords = create_words(Word);
        if (NULL != newWords) {
                newWords->next = wordList;
        }
        return newWords;
}

int main(int argc, char** argv) {

        words* myWords = create_words("Hello");
        myWords = add_Word(myWords, "World");

        words* iter;
        for (iter = myWords; NULL != iter; iter = iter->next) {
                printf("%s ", iter->str);
        }
        delete_words(myWords);
        return 0;
}

Oui, désolé pour la réponse la plus longue du monde. Donc, WRT au "ne veulent pas utiliser un commentaire de liste chaînée":

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

typedef struct {
    char** words;
    size_t nWords;
    size_t size;
    size_t block_size;
} Word_list;

Word_list* create_Word_list(size_t block_size) {
    Word_list* pWordList = malloc(sizeof(Word_list));
    if (NULL != pWordList) {
        pWordList->nWords = 0;
        pWordList->size = block_size;
        pWordList->block_size = block_size;
        pWordList->words = malloc(sizeof(char*)*block_size);
        if (NULL == pWordList->words) {
            free(pWordList);
            return NULL;    
        }
    }
    return pWordList;
}

void delete_Word_list(Word_list* pWordList) {
    free(pWordList->words);
    free(pWordList);
}

int add_Word_to_Word_list(Word_list* pWordList, char* Word) {
    size_t nWords = pWordList->nWords;
    if (nWords >= pWordList->size) {
        size_t newSize = pWordList->size + pWordList->block_size;
        void* newWords = realloc(pWordList->words, sizeof(char*)*newSize); 
        if (NULL == newWords) {
            return 0;
        } else {    
            pWordList->size = newSize;
            pWordList->words = (char**)newWords;
        }

    }

    pWordList->words[nWords] = Word;
    ++pWordList->nWords;


    return 1;
}

char** Word_list_start(Word_list* pWordList) {
        return pWordList->words;
}

char** Word_list_end(Word_list* pWordList) {
        return &pWordList->words[pWordList->nWords];
}

int main(int argc, char** argv) {

        Word_list* myWords = create_Word_list(2);
        add_Word_to_Word_list(myWords, "Hello");
        add_Word_to_Word_list(myWords, "World");
        add_Word_to_Word_list(myWords, "Goodbye");

        char** iter;
        for (iter = Word_list_start(myWords); iter != Word_list_end(myWords); ++iter) {
                printf("%s ", *iter);
        }

        delete_Word_list(myWords);

        return 0;
}
35
Tom

Si vous souhaitez allouer dynamiquement des tableaux, vous pouvez utiliser malloc from stdlib.h.

Si vous souhaitez allouer un tableau de 100 éléments à l'aide de votre structure words, procédez comme suit:

words* array = (words*)malloc(sizeof(words) * 100);

La taille de la mémoire que vous souhaitez allouer est passée à malloc. Un pointeur de type void (void*) sera ensuite renvoyé. Dans la plupart des cas, vous souhaiterez probablement le transtyper vers le type de pointeur souhaité, qui est dans ce cas words*.

Le mot clé sizeof est utilisé ici pour connaître la taille de la structure words. Cette taille est ensuite multipliée par le nombre d'éléments que vous souhaitez allouer.

Une fois que vous avez terminé, veillez à utiliser free() pour libérer la mémoire de tas que vous avez utilisée afin de prévenir les fuites de mémoire :

free(array);

Si vous voulez changer la taille du tableau alloué, vous pouvez essayer d'utiliser realloc comme d'autres l'ont mentionné, mais gardez à l'esprit que si vous faites beaucoup de realloc, vous risquez de vous retrouver avec fragmenter la mémoire . Si vous souhaitez redimensionner dynamiquement le tableau afin de conserver une faible empreinte mémoire pour votre programme, il peut être préférable de ne pas en faire trop de reallocs.

11
coobird

Cela ressemble à un exercice académique qui rend malheureusement la tâche plus difficile puisque vous ne pouvez pas utiliser le C++. En gros, vous devez gérer une partie des frais généraux liés à l'allocation et garder trace de la quantité de mémoire allouée si vous devez la redimensionner ultérieurement. C'est là que brille la bibliothèque standard C++.

Pour votre exemple, le code suivant alloue la mémoire et la redimensionne ultérieurement:

// initial size
int count = 100;
words *testWords = (words*) malloc(count * sizeof(words));
// resize the array
count = 76;
testWords = (words*) realloc(testWords, count* sizeof(words));

Gardez à l'esprit que, dans votre exemple, vous n'attribuez qu'un pointeur à un caractère et que vous devez toujours attribuer la chaîne elle-même et, plus important encore, la libérer à la fin. Donc, ce code alloue 100 caractères à char, puis le redimensionne à 76, mais n'alloue pas les chaînes elles-mêmes.

Je soupçonne que vous voulez réellement affecter le nombre de caractères d'une chaîne, ce qui est très similaire à ce qui précède, mais changez Word en char.

EDIT: N'oubliez pas non plus qu'il est très logique de créer des fonctions permettant d'exécuter des tâches courantes et d'appliquer la cohérence afin d'éviter de copier le code partout. Par exemple, vous pourriez avoir a) allouer la structure, b) attribuer des valeurs à la structure et c) libérer la structure. Donc vous pourriez avoir:

// Allocate a words struct
words* CreateWords(int size);
// Assign a value
void AssignWord(Word* dest, char* str);
// Clear a words structs (and possibly internal storage)
void FreeWords(words* w);

EDIT: En ce qui concerne le redimensionnement des structures, il est identique au redimensionnement du tableau de caractères. Cependant, la différence est que si vous agrandissez le tableau struct, vous devez probablement initialiser les nouveaux éléments du tableau sur NULL. De même, si vous réduisez la taille du tableau struct, vous devez nettoyer avant de supprimer les éléments, c’est-à-dire les éléments libres qui ont été alloués (et uniquement les éléments alloués) avant de redimensionner le tableau struct. C'est la raison principale pour laquelle j'ai suggéré de créer des fonctions d'assistance pour aider à gérer cela.

// Resize words (must know original and new size if shrinking
// if you need to free internal storage first)
void ResizeWords(words* w, size_t oldsize, size_t newsize);
5
Ryan

En C++, utilisez un vector . C'est comme un tableau, mais vous pouvez facilement ajouter et supprimer des éléments, ce qui vous permettra d'allouer et de désallouer de la mémoire.

Je connais le titre de la question dit C, mais vous avez marqué votre question avec C et C++ ...

3
Jeremy Ruten

Une autre option pour vous est une liste liée . Vous devrez analyser la manière dont votre programme utilisera la structure de données. Si vous n'avez pas besoin d'un accès aléatoire, cela pourrait être plus rapide que la réaffectation.

3
Neil Williams

Voici comment je le ferais en C++

size_t size = 500;
char* dynamicAllocatedString = new char[ size ];

Utilisez le même principe pour toutes les classes struct ou c ++.

3
Adrian

Votre code dans la dernière mise à jour ne doit pas être compilé, et encore moins exécuté. Vous passez & x à LoadData. & x a le type de ** mots, mais LoadData attend des mots *. Bien sûr, il se bloque lorsque vous appelez realloc sur un pointeur pointant dans la pile.

La solution consiste à changer LoadData pour accepter les mots **. Cela dit, vous pouvez réellement modifier le pointeur dans main (). Par exemple, l'appel realloc ressemblerait à

*x = (words*) realloc(*x, sizeof(words)*2);

C'est le même principe que dans "num" étant int * plutôt qu'int.

En plus de cela, vous devez vraiment comprendre comment les chaînes de mots sont stockées. L'attribution d'une chaîne const à char * (comme dans str2 = "marley\0") est autorisée, mais c'est rarement la bonne solution, même en C.

Autre point: il n’est pas nécessaire d’avoir "marley\0" sauf si vous avez vraiment besoin de deux 0 en fin de chaîne. Le compilateur ajoute 0 jusqu'à la fin de chaque littéral de chaîne.

1
Arkadiy

Pour le code de test: si vous souhaitez modifier un pointeur dans une fonction, vous devez passer un "pointeur à pointeur" à la fonction. Le code corrigé est le suivant:

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

typedef struct
{
    char *str1;
    char *str2;
} words;

void LoadData(words**, int*);

main()
{
    words **x;
    int num;

    LoadData(x, &num);

    printf("%s %s\n", (*x[0]).str1, (*x[0]).str2);
    printf("%s %s\n", (*x[1]).str1, (*x[1]).str2);
}

void LoadData(words **x, int *num)
{
    *x = (words*) malloc(sizeof(words));

    (*x[0]).str1 = "johnnie\0";
    (*x[0]).str2 = "krapson\0";

    *x = (words*) realloc(*x, sizeof(words) * 2);
    (*x[1]).str1 = "bob\0";
    (*x[1]).str2 = "marley\0";

    *num = *num + 1;
}
1
zhanwu

Si vous souhaitez agrandir le tableau de manière dynamique, vous devez utiliser malloc () pour allouer de manière dynamique une quantité de mémoire fixe, puis utiliser realloc () chaque fois que vous en manquez. Une technique courante consiste à utiliser une fonction de croissance exponentielle telle que vous allouiez un petit montant fixe, puis fassiez croître le tableau en dupliquant le montant alloué. 

Un exemple de code serait:

size = 64; i = 0;
x = malloc(sizeof(words)*size); /* enough space for 64 words */
while (read_words()) {
    if (++i > size) {
        size *= 2;
        x = realloc(sizeof(words) * size);
    }
}
/* done with x */
free(x);
0
ob1

Chaque codeur doit simplifier son code pour le rendre facilement compréhensible ... même pour les débutants.

Donc le tableau de structures utilisant dynamiquement est facile, si vous comprenez les concepts.

// Dynamically sized array of structures

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

struct book 
{
    char name[20];
    int p;
};              //Declaring book structure

int main () 
{
    int n, i;      

    struct book *b;     // Initializing pointer to a structure
    scanf ("%d\n", &n);

    b = (struct book *) calloc (n, sizeof (struct book));   //Creating memory for array of structures dynamically

    for (i = 0; i < n; i++)
    {
        scanf ("%s %d\n", (b + i)->name, &(b + i)->p);  //Getting values for array of structures (no error check)
    }          

    for (i = 0; i < n; i++)
    {
        printf ("%s %d\t", (b + i)->name, (b + i)->p);  //Printing values in array of structures
    }

    scanf ("%d\n", &n);     //Get array size to re-allocate    
    b = (struct book *) realloc (b, n * sizeof (struct book));  //change the size of an array using realloc function
    printf ("\n");

    for (i = 0; i < n; i++)
    {
        printf ("%s %d\t", (b + i)->name, (b + i)->p);  //Printing values in array of structures
    }

    return 0;
}   
0
Arul Girish