Как я могу создать динамический размер массива структур? - PullRequest
49 голосов
/ 04 ноября 2008

Я знаю, как создать массив структур, но с предопределенным размером. Однако есть ли способ создать динамический массив структур, чтобы массив мог увеличиваться?

Например:

    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.
    }

Возможно ли это?


Я исследовал это: words* array = (words*)malloc(sizeof(words) * 100);

Я хочу избавиться от 100 и сохранять данные по мере их поступления. Таким образом, если поступает 76 полей данных, я хочу хранить 76, а не 100. Я предполагаю, что не знаю, сколько данные поступают в мою программу. В структуре, которую я определил выше, я мог создать первый «индекс» как:

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

Однако я хочу динамически добавлять элементы в массив после. Надеюсь, я достаточно четко описал проблемную область. Основная задача состоит в том, чтобы динамически добавить второе поле, по крайней мере, на данный момент это проблема.


Однако я немного продвинулся:

    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?

Я сделал некоторую проверку ошибок, и это то, что я нашел. Если после освобождения памяти для x я добавлю следующее:

    x=NULL;

тогда, если я пытаюсь напечатать x, я получаю сообщение об ошибке, которое я и хочу. Так что, бесплатная функция не работает, по крайней мере, на моем компиляторе? Я использую DevC ??


Спасибо, теперь я понимаю из-за:

FirstName - это указатель на массив char, который не выделяется malloc, только указатель выделяется и после вызова free не стирает память, он просто помечает ее как доступную в куче быть переписано позже. - MattSmith

Обновление

Я пытаюсь модулировать и поместить создание моего массива структур в функцию, но, похоже, ничего не работает. Я пытаюсь что-то очень простое, и я не знаю, что еще делать. Это то же самое, что и раньше, просто еще одна функция, loaddata, которая загружает данные, и вне метода, который мне нужен для печати. Как я могу заставить это работать? Мой код выглядит следующим образом:

    # 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;
    }//

Этот простой тестовый код дает сбой, и я понятия не имею, почему. Где ошибка?

Ответы [ 10 ]

35 голосов
/ 04 ноября 2008

Вы пометили это как C ++, а также C.

Если вы используете C ++, все намного проще. Стандартная библиотека шаблонов имеет шаблон под названием вектор, который позволяет динамически создавать список объектов.

#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;
}

Если вы используете C, все намного сложнее, да, malloc, realloc и free - инструменты, которые вам помогут. Возможно, вы захотите использовать вместо этого структуру данных связанного списка. Как правило, их легче вырастить, но они не облегчают произвольный доступ.

#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;
}

Yikes, прости за самый длинный в мире ответ. Так что пишите "не хочу использовать связанный список комментариев":

#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;
}
11 голосов
/ 04 ноября 2008

Если вы хотите динамически распределять массивы, вы можете использовать malloc из stdlib.h.

Если вы хотите выделить массив из 100 элементов, используя структуру words, попробуйте следующее:

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

Размер памяти, которую вы хотите выделить, передается в malloc, а затем возвращается указатель типа void (void*). В большинстве случаев вы, вероятно, захотите привести его к желаемому типу указателя, который в данном случае равен words*.

Ключевое слово sizeof используется здесь, чтобы узнать размер структуры words, затем этот размер умножается на количество элементов, которые вы хотите выделить.

Когда вы закончите, обязательно используйте free(), чтобы освободить используемую память кучи, чтобы предотвратить утечки памяти :

free(array);

Если вы хотите изменить размер выделенного массива, вы можете попробовать использовать realloc, как уже упоминали другие, но имейте в виду, что если вы сделаете много realloc s, вы можете в итоге фрагментировать память . Если вы хотите динамически изменить размер массива, чтобы сохранить малый объем памяти для вашей программы, возможно, лучше не делать слишком много realloc s.

5 голосов
/ 04 ноября 2008

Это похоже на учебное упражнение, которое, к сожалению, усложняет задачу, поскольку вы не можете использовать C ++. По сути, вам приходится управлять некоторыми накладными расходами на распределение и отслеживать, сколько памяти было выделено, если вам нужно изменить размер позже. Вот где сияет стандартная библиотека C ++.

Для вашего примера следующий код выделяет память, а затем изменяет ее размер:

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

Имейте в виду, что в вашем примере вы просто назначаете указатель на символ, и вам все еще нужно выделить саму строку и, что более важно, освободить ее в конце. Таким образом, этот код выделяет 100 указателей на char, а затем изменяет размер до 76, но не выделяет сами строки.

У меня есть подозрение, что вы на самом деле хотите выделить количество символов в строке, что очень похоже на приведенное выше, но измените слово на char.

РЕДАКТИРОВАТЬ: также имейте в виду, что имеет большой смысл создавать функции для выполнения общих задач и обеспечивать согласованность, чтобы вы не копировали код повсюду. Например, у вас может быть а) выделение структуры, б) присвоение значений структуре и в) освобождение структуры. Таким образом, вы можете иметь:

// 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);

РЕДАКТИРОВАТЬ: Что касается изменения размеров структур, он идентичен изменению размера массива символов. Однако разница в том, что если вы увеличите массив struct, вам, вероятно, следует инициализировать новые элементы массива в NULL. Точно так же, если вы уменьшите массив struct, вам необходимо очистить его перед удалением элементов - это свободные элементы, которые были выделены (и только выделенные элементы), прежде чем изменить размер массива struct. Это основная причина, по которой я предложил создать вспомогательные функции, чтобы помочь в этом.

// 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);
3 голосов
/ 14 июля 2011

Вот как бы я это сделал в C ++

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

Используйте один и тот же принципал для любого класса struct или c ++.

3 голосов
/ 04 ноября 2008

Другой вариант для вас - это связанный список . Вам нужно проанализировать, как ваша программа будет использовать структуру данных, если вам не нужен произвольный доступ, это может быть быстрее, чем перераспределение.

3 голосов
/ 04 ноября 2008

В C ++ используйте vector . Это как массив, но вы можете легко добавлять и удалять элементы, и он позаботится о выделении и освобождении памяти для вас.

Я знаю, что в заголовке вопроса написано C, но вы пометили свой вопрос C и C ++ ...

1 голос
/ 04 ноября 2011

Для тестового кода: если вы хотите изменить указатель в функции, вы должны передать «указатель на указатель» в функцию. Исправленный код выглядит следующим образом:

#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 голос
/ 05 ноября 2008

Ваш код в последнем обновлении не должен компилироваться, тем более запускаться. Вы передаете & x в LoadData. & x имеет тип ** слов, но LoadData ожидает слова *. Конечно, происходит сбой при вызове realloc для указателя, указывающего на стек.

Способ исправить это - изменить LoadData так, чтобы он принимал слова **. Таким образом, вы можете изменить указатель в main (). Например, вызов realloc будет выглядеть как

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

Это те же принципы, что и в "num": int *, а не int.

Кроме того, вам нужно по-настоящему понять, как хранятся строки в словах. Назначение константной строки для char * (как в str2 = "marley \ 0") разрешено, но это редко является правильным решением, даже в C.

Еще один момент: не нужно иметь «marley \ 0», если только вам действительно не нужны два 0 в конце строки. Компилятор добавляет 0 к концу каждого строкового литерала.

0 голосов
/ 03 мая 2018

Каждый кодер должен упростить свой код, чтобы его было легко понять .... даже для начинающих.

Итак, массив структур, использующих динамически , очень просто, если вы понимаете концепции.

// 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 голосов
/ 04 ноября 2008

Если вы хотите динамически увеличивать массив, вы должны использовать malloc () для динамического выделения некоторого фиксированного объема памяти, а затем использовать realloc () всякий раз, когда у вас заканчивается. Обычный способ - использовать функцию экспоненциального роста, чтобы вы выделяли небольшую фиксированную сумму, а затем увеличивали массив путем дублирования выделенной суммы.

Пример кода:

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);
...