Як і у всьому, що спочатку здається страшнішим, ніж пізніше, найкращий спосіб подолати початковий страх - зануритись у дискомфорт невідомого ! Це часом, як ми дізнаємося найбільше.
На жаль, існують обмеження. Хоча ви все ще вчитеся користуватися функцією, ви не повинні брати на себе роль вчителя, наприклад. Я часто читаю відповіді від тих, хто, здається, не знає, як їх використовувати 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); коли ви закінчите з цим!