Как я могу создать динамический размер массива структур?

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

Например:

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

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

11 ответов

Вы пометили это как 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;
}

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

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

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

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

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

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

free(array);

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

Это похоже на академическое упражнение, которое, к сожалению, усложняет задачу, поскольку вы не можете использовать 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);

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

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

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

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

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

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

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

Таким образом, массив структур использовать динамически легко, если вы понимаете концепции.

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

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

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

Ваш код в последнем обновлении не должен компилироваться, тем более запускаться. Вы передаете &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 до конца каждого строкового литерала.

Если вы хотите динамически увеличивать массив, вы должны использовать 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);

Проверять, выписываться malloc а также realloc,

Другие вопросы по тегам