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

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

Например:

    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 is a pointer to an array of char which is not being allocated by the malloc, only the pointer is being allocated and after you call free, it doesn't erase the memory, it just marks it as available on the heap to be over written later. – 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;
    }//

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

Что именно вы имеете в виду, говоря: «Я хочу динамически добавлять элементы в массив после» и «Основная задача - динамически добавить второе поле»? Вы имеете в виду добавление второго элемента к элементу внутри структуры? Возможно, вам понадобятся: typedef struct {char ** str} words; Указатель на строку.

Ryan 04.11.2008 09:27

Никогда не делайте x = realloc (x, newsize); если у вас нет копии значения x. если realloc () не работает, значит, вы потеряли указатель и произошла утечка.

Chris Young 04.11.2008 09:43

Ответ на мою проблему был в вашем вопросе :)

Greg 29.01.2012 15:43
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
54
3
177 892
10

Ответы 10

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

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

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

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

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

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

free(array);

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

Спасибо, что указали на это, Ночной всадник! Я исправил ошибку.

coobird 04.11.2008 08:20

В C не обязательно, чтобы 'void *' (возвращаемый функцией malloc ()) приводился к другому типу указателя - вы можете опустить приведение. Однако C++ требует приведения.

Jonathan Leffler 04.11.2008 10:46

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

Это неправильное использование realloc. Пожалуйста, исследуйте и отредактируйте соответствующим образом.

Chris Young 04.11.2008 08:26

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

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

Я голосую против, потому что это был верхний ответ, но теперь тег равен только c. Прости ;)

elmarco 11.05.2009 15:50

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

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

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

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

Ой, извините за самый длинный ответ в мире. Итак, WRT к "не хочу использовать комментарий связанного списка":

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

У меня нет выбора, кроме как использовать динамический массив. Если бы это был связанный список, у меня не было бы проблем. Я написал множество программ со связанными списками, поэтому эта программа кажется мне такой сложной.

D. Rattansingh 04.11.2008 09:04

Не приводите возвращаемые значения функций, которые возвращают void * в C. Это ничего не добавляет и потенциально может скрыть ошибку отсутствующего прототипа.

Chris Young 04.11.2008 09:42

Крис, вам всегда нужно преобразовать возврат от void * к тому, что вы выделили - это не C++.

Tom 04.11.2008 10:16

@thaggie: у вас все наоборот; C не требует приведения, но C++ требует.

Jonathan Leffler 04.11.2008 10:49

@thaggie (снова): интересный перенос итераторов STL в C - хорошая идея. Вы не проверяете возвращаемое значение из add_word_to_list (), а жаль.

Jonathan Leffler 04.11.2008 10:54

@ Chris / Jonathan - я поправлюсь - я слишком давно не использовал компилятор C.

Tom 04.11.2008 19:37

@thaggie: Очень мило, мне это нравится. Одна потенциальная проблема - статические строки и add_word_to_word_list. Ваш пример отлично подходит для статических строк, но для выделенных строк это вызовет утечку памяти. Возможно, лучше выделить память и скопировать строку, а затем освободить ее в delete_word_list

Ryan 04.11.2008 21:05

@ryan - Естественно - нужно оставить что-нибудь в качестве упражнения для читателя, хотя, конечно?

Tom 05.11.2008 07:42

Чтобы добавить к вышесказанному, вы можете подумать об использовании некоторых для выделения «кусками». realloc - дорогостоящая операция, и вы хотите делать это экономно, особенно, если вас так сильно беспокоит эффективность памяти, вам, вероятно, также нужно следить за переключениями между операциями ядра и пользовательского пространства.

shiva 14.01.2011 05:13

Что означает параметр block_size для функции create_word_list ()? Что такое размер блока?

Max 08.10.2011 01:49

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

Tom 08.10.2011 08:57

Значение более или менее произвольно? Когда имеет смысл увеличивать / уменьшать размер блока?

Max 08.10.2011 15:08

Связанный список - очень хорошая идея, а utlist.h - удобная реализация.

Craig McMahon 12.07.2012 10:37

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

Имейте в виду, что в вашем примере вы просто выделяете указатель на char, и вам все равно нужно выделить саму строку и, что более важно, освободить ее в конце. Таким образом, этот код выделяет 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);

Обновлено: Что касается изменения размера структур, это идентично изменению размера массива char. Однако разница в том, что если вы увеличиваете структурный массив, вам, вероятно, следует инициализировать новые элементы массива значением NULL. Точно так же, если вы сделаете массив структур меньше, вам необходимо очистить перед удалением элементов, то есть свободных элементов, которые были выделены (и только выделенные элементы), прежде чем вы измените размер массива структур. Это основная причина, по которой я предложил создать вспомогательные функции, которые помогут справиться с этим.

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

Да, это прискорбно, когда вы знакомы с хорошими программами, такими как java и т. д., И вы возвращаетесь к старым временам с помощью c. Да, я хочу выделить строку из моего ответа ниже. На самом деле я знаю, как динамически распределять символы в строке. Проблема в работе с массивом структур.

D. Rattansingh 04.11.2008 09:01

Вы отбрасываете возвращаемое значение realloc. Это неправильно: (а) realloc может дать сбой (вернуть NULL), (б) realolc может вернуть новый указатель, потому что ему пришлось переместить данные.

Chris Young 04.11.2008 09:41

Учитывая, что изменение размера - это операция сжатия, очень маловероятно, что realloc () сместит данные. Тем не менее, как вы предлагаете, думать, что realloc () не будет перемещать данные, - плохая идея, особенно если вы когда-нибудь увеличите массив.

Jonathan Leffler 04.11.2008 10:44

@Chris Young - Согласен, я слишком быстро посмотрел на прототип для realloc и подумал, что он вернул void вместо void *. Используйте возвращаемое значение для памяти.

Ryan 04.11.2008 20:58

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

«Назначение константной строки для char * (как в str2 = " marley \ 0 ") разрешено, но это редко правильное решение, даже в C.» Это единственное известное мне решение. Есть ли лучшее решение, чем присвоение строкового значения?

D. Rattansingh 05.11.2008 23:15

Вы назначаете строковую константу. Строковое значение может быть присвоено, например, с помощью strdup: str2 = strdup ("marley"). Таким образом, за освобождение памяти отвечает владелец структуры слов. Я действительно думаю, что вам было бы лучше использовать более щадящий язык, например Python или даже Java.

user3458 06.11.2008 16:05

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

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

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

Вопрос в C, а не в C++

LKM 10.05.2016 17:17

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

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

вы можете изменить только функцию: void LoadData(words **y, int * num) { words *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; *y= x; }

Boklucius 04.01.2012 02:18

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

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

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

Я не думаю, что это решает суть вопроса: изменение размера массива после его создания.

David Hempy 03.05.2018 17:10

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