Как и все, что сначала кажется более пугающим, чем было позже, лучший способ преодолеть первоначальный страх - погрузиться в дискомфорт неизвестного ! В конце концов, иногда это похоже на то, чему мы учимся больше всего.
К сожалению, есть ограничения. Пока вы все еще учитесь использовать функцию, вам, например, не следует брать на себя роль учителя. Я часто читаю ответы тех, кто, казалось бы, не знает, как использовать 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);
когда вы закончите с этим!