Я знаю, как создать массив структур, но с предопределенным размером. Однако есть ли способ создать динамический массив структур, чтобы массив мог увеличиваться?
Например:
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;
}//
Этот простой тестовый код дает сбой, и я понятия не имею, почему. Где ошибка?
Никогда не делайте x = realloc (x, newsize); если у вас нет копии значения x. если realloc () не работает, значит, вы потеряли указатель и произошла утечка.
Ответ на мою проблему был в вашем вопросе :)





Если вы хотите динамически размещать массивы, вы можете использовать malloc из stdlib.h.
Если вы хотите выделить массив из 100 элементов с помощью структуры words, попробуйте следующее:
words* array = (words*)malloc(sizeof(words) * 100);
Размер памяти, которую вы хотите выделить, передается в malloc, а затем он возвращает указатель типа void (void*). В большинстве случаев вы, вероятно, захотите привести его к желаемому типу указателя, которым в данном случае является words*.
Ключевое слово sizeof используется здесь, чтобы узнать размер структуры words, затем этот размер умножается на количество элементов, которые вы хотите выделить.
Как только вы закончите, обязательно используйте free(), чтобы освободить память кучи, которую вы использовали, чтобы предотвратить утечки памяти:
free(array);
Если вы хотите изменить размер выделенного массива, вы можете попробовать использовать realloc, как упоминали другие, но имейте в виду, что если вы сделаете много realloc, вы можете получить фрагментация памяти. Если вы хотите динамически изменять размер массива, чтобы сохранить небольшой объем памяти для вашей программы, возможно, лучше не делать слишком много realloc.
Спасибо, что указали на это, Ночной всадник! Я исправил ошибку.
В C не обязательно, чтобы 'void *' (возвращаемый функцией malloc ()) приводился к другому типу указателя - вы можете опустить приведение. Однако C++ требует приведения.
Если вы хотите динамически увеличивать массив, вы должны использовать 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. Пожалуйста, исследуйте и отредактируйте соответствующим образом.
В C++ используйте вектор. Это похоже на массив, но вы можете легко добавлять и удалять элементы, и он позаботится о выделении и освобождении памяти за вас.
Я знаю, что в заголовке вопроса написано C, но вы пометили свой вопрос с помощью C и C++ ...
Я голосую против, потому что это был верхний ответ, но теперь тег равен только c. Прости ;)
Другой вариант для вас - связанный список. Вам нужно будет проанализировать, как ваша программа будет использовать структуру данных, если вам не нужен произвольный доступ, это может быть быстрее, чем перераспределение.
Вы отметили это как 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;
}
У меня нет выбора, кроме как использовать динамический массив. Если бы это был связанный список, у меня не было бы проблем. Я написал множество программ со связанными списками, поэтому эта программа кажется мне такой сложной.
Не приводите возвращаемые значения функций, которые возвращают void * в C. Это ничего не добавляет и потенциально может скрыть ошибку отсутствующего прототипа.
Крис, вам всегда нужно преобразовать возврат от void * к тому, что вы выделили - это не C++.
@thaggie: у вас все наоборот; C не требует приведения, но C++ требует.
@thaggie (снова): интересный перенос итераторов STL в C - хорошая идея. Вы не проверяете возвращаемое значение из add_word_to_list (), а жаль.
@ Chris / Jonathan - я поправлюсь - я слишком давно не использовал компилятор C.
@thaggie: Очень мило, мне это нравится. Одна потенциальная проблема - статические строки и add_word_to_word_list. Ваш пример отлично подходит для статических строк, но для выделенных строк это вызовет утечку памяти. Возможно, лучше выделить память и скопировать строку, а затем освободить ее в delete_word_list
@ryan - Естественно - нужно оставить что-нибудь в качестве упражнения для читателя, хотя, конечно?
Чтобы добавить к вышесказанному, вы можете подумать об использовании некоторых для выделения «кусками». realloc - дорогостоящая операция, и вы хотите делать это экономно, особенно, если вас так сильно беспокоит эффективность памяти, вам, вероятно, также нужно следить за переключениями между операциями ядра и пользовательского пространства.
Что означает параметр block_size для функции create_word_list ()? Что такое размер блока?
Он выделяет место для слов в блоках, это количество слов, которые умещаются в блоке.
Значение более или менее произвольно? Когда имеет смысл увеличивать / уменьшать размер блока?
Связанный список - очень хорошая идея, а utlist.h - удобная реализация.
Это похоже на академическое упражнение, которое, к сожалению, усложняет задачу, поскольку вы не можете использовать 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. Да, я хочу выделить строку из моего ответа ниже. На самом деле я знаю, как динамически распределять символы в строке. Проблема в работе с массивом структур.
Вы отбрасываете возвращаемое значение realloc. Это неправильно: (а) realloc может дать сбой (вернуть NULL), (б) realolc может вернуть новый указатель, потому что ему пришлось переместить данные.
Учитывая, что изменение размера - это операция сжатия, очень маловероятно, что realloc () сместит данные. Тем не менее, как вы предлагаете, думать, что realloc () не будет перемещать данные, - плохая идея, особенно если вы когда-нибудь увеличите массив.
@Chris Young - Согласен, я слишком быстро посмотрел на прототип для realloc и подумал, что он вернул void вместо void *. Используйте возвращаемое значение для памяти.
Ваш код в последнем обновлении не должен компилироваться, а тем более запускаться. Вы передаете & 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.» Это единственное известное мне решение. Есть ли лучшее решение, чем присвоение строкового значения?
Вы назначаете строковую константу. Строковое значение может быть присвоено, например, с помощью strdup: str2 = strdup ("marley"). Таким образом, за освобождение памяти отвечает владелец структуры слов. Я действительно думаю, что вам было бы лучше использовать более щадящий язык, например Python или даже Java.
Вот как я бы сделал это на C++
size_t size = 500;
char* dynamicAllocatedString = new char[ size ];
Используйте тот же принцип для любой структуры или класса C++.
Вопрос в C, а не в C++
Для тестового кода: если вы хотите изменить указатель в функции, вы должны передать в функцию «указатель на указатель». Исправленный код выглядит следующим образом:
#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; }
Каждому программисту необходимо упростить свой код, чтобы его было легко понять ... даже для новичков.
Так что массив структур, использующий динамически легко, если вы понимаете концепции.
// 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;
}
Я не думаю, что это решает суть вопроса: изменение размера массива после его создания.
Что именно вы имеете в виду, говоря: «Я хочу динамически добавлять элементы в массив после» и «Основная задача - динамически добавить второе поле»? Вы имеете в виду добавление второго элемента к элементу внутри структуры? Возможно, вам понадобятся: typedef struct {char ** str} words; Указатель на строку.