Як і у всьому, що спочатку здається страшнішим, ніж пізніше, найкращий спосіб подолати початковий страх - зануритись у дискомфорт невідомого ! Це часом, як ми дізнаємося найбільше.
На жаль, існують обмеження. Хоча ви все ще вчитеся користуватися функцією, ви не повинні брати на себе роль вчителя, наприклад. Я часто читаю відповіді від тих, хто, здається, не знає, як їх використовувати realloc
(тобто, прийняту на даний момент відповідь! ), Розповідаючи іншим, як ним користуватися неправильно, періодично під виглядом того, що вони пропустили поводження з помилками , хоча це звичайна помилка. про яку потрібно згадати. Ось відповідь, що пояснює, як realloc
правильно користуватися . Зверніть увагу, що відповідь зберігає повернене значення в іншу змінну, щоб здійснити перевірку помилок.
Кожен раз, коли ви викликаєте функцію та кожного разу, коли ви використовуєте масив, ви використовуєте вказівник. Перетворення відбуваються неявно, що якщо що-небудь має бути ще страшніше, оскільки саме ті речі, яких ми не бачимо, часто викликають найбільші проблеми. Наприклад, витоки пам'яті ...
Оператори масиву - оператори вказівників. array[x]
це дійсно ярлик для *(array + x)
, який можна розбити на: *
і (array + x)
. Найімовірніше, що *
саме те бентежить. Ми можемо додатково усунути додавання від проблеми, вважаючи, x
що це 0
, таким чином, array[0]
стає *array
тому, що додавання0
не змінить значення ...
... і таким чином ми можемо побачити, що *array
рівнозначно array[0]
. Ви можете використовувати одне там, де ви хочете використовувати інше, і навпаки. Оператори масиву - оператори вказівників.
malloc
, realloc
а друзі не вигадують поняття вказівника, яким ви користуєтесь весь час; вони просто використовують це для реалізації якоїсь іншої функції, яка є різною формою тривалості зберігання, найбільш підходящою, коли ви бажаєте різких, динамічних змін розміру .
Прикро, що прийнята в даний час відповідь також суперечить зерну деяких інших дуже обґрунтованих порад щодо StackOverflow , і в той же час пропускає можливість запровадити маловідому функцію, яка світить саме для цього використання: гнучкий масив члени! Це насправді досить зламана відповідь ... :(
Коли ви визначаєте свою struct
, оголошуйте масив наприкінці структури, без верхньої межі. Наприклад:
struct int_list {
size_t size;
int value[];
};
Це дозволить вам об'єднати масив int
у те саме виділення count
, що і ваше , і пов'язати їх так, як це може бути дуже зручно !
sizeof (struct int_list)
буде діяти так, ніби value
має розмір 0, тому він розповість вам розмір структури з порожнім списком . Ще потрібно додати розмір, переданий доrealloc
щоб вказати розмір списку.
Ще одна підказка - пам’ятати, що realloc(NULL, x)
це рівнозначно malloc(x)
, і ми можемо використовувати це для спрощення нашого коду. Наприклад:
int push_back(struct int_list **fubar, int value) {
size_t x = *fubar ? fubar[0]->size : 0
, y = x + 1;
if ((x & y) == 0) {
void *temp = realloc(*fubar, sizeof **fubar
+ (x + y) * sizeof fubar[0]->value[0]);
if (!temp) { return 1; }
*fubar = temp; // or, if you like, `fubar[0] = temp;`
}
fubar[0]->value[x] = value;
fubar[0]->size = y;
return 0;
}
struct int_list *array = NULL;
Причина, яку я вирішив використовувати struct int_list **
як перший аргумент, може здатися не очевидною відразу, але якщо ви подумаєте про другий аргумент, будь-які зміни, внесені value
зсередини push_back
, не будуть видні функції, з якої ми викликаємо, правда? Те ж саме стосується першого аргументу, і нам потрібно вміти змінювати наші array
, не тільки тут, але, можливо, і в будь-якій іншій функції / функції, на яку ми передаємо його ...
array
починає вказувати на ніщо; це порожній список. Ініціалізація це те саме, що і додавання до неї. Наприклад:
struct int_list *array = NULL;
if (!push_back(&array, 42)) {
// success!
}
PS Пам'ятайте,free(array);
коли ви закінчите з цим!