Копирование указателей для двух динамически растущих массивов в C
ОБНОВЛЕНИЕ: я думаю, что я ответил на свой собственный вопрос, за исключением некоторых возможных проблем с утечками памяти.
ОРИГИНАЛЬНЫЙ ВОПРОС ЗДЕСЬ, ОТВЕТЬ НИЖЕ.
Предыстория: я занимаюсь некоторыми числовыми вычислениями, но я почти никогда не использую языки, которые требуют, чтобы я управлял памятью самостоятельно. Я сейчас что-то передаю в C и испытываю проблемы с (я думаю) ссылками на указатели.
У меня есть два массива значений типа double, которые растут в цикле while, и на каждой итерации я хочу освободить память для меньшего, более старого массива, установить 'old', чтобы он указывал на новый массив, а затем установить 'new' в указать на больший блок памяти.
Посмотрев немного, мне показалось, что я должен использовать указатели для указателей, поэтому я попробовал это, но столкнулся с ошибками "lvalue required as unary" & "operand".
Я начинаю с:
double ** oldarray;
oldarray = &malloc(1*sizeof(double));
double ** newarray;
newarray = &malloc(2*sizeof(double));
Эти инициализации дают мне ошибку "lvalue Требуется как унарный" и "операнд", и я не уверен, должен ли я заменить ее на
*oldarray = (double *) malloc(1*sizeof(double));
Когда я делаю это, я могу скомпилировать простую программу (она просто содержит строки, которые у меня есть выше, и возвращает 0), но я получаю ошибку сегмента.
Остальная часть программы выглядит следующим образом:
while ( <some condition> ) {
// Do a lot of processing, most of which is updating
// the values in newarray using old array and other newarray values.
// Now I'm exiting the loop, and growing and reset ing arrays.
free(*oldarray) // I want to free the memory used by the smaller, older array.
*oldarray = *newarray // Then I want oldarray to point to the larger, newer array.
newarray = &malloc( <previous size + 1>*sizeof(double))
}
Поэтому я бы хотел, чтобы на каждой итерации обновлялся массив размера (n) с использованием самого себя и более старого массива размера (n-1). Затем я хочу освободить память массива размера (n-1), установить 'oldarray', чтобы он указывал на только что созданный массив, а затем установить 'newarray', чтобы он указывал на новый блок размера (n+1).) удваивается.
Мне действительно нужно использовать указатели на указатели? Я думаю, что моя главная проблема заключается в том, что, когда я устанавливаю old на new, они разделяют pointee, и тогда я не знаю, как установить new для нового массива. Я думаю, что использование указателей на указатели выводит меня из этого, но я не уверен, и у меня все еще есть ошибки lvalue с указателями на указатели.
Я проверил динамически растущий массив C и несколько других вопросов о стеке, и примерно полдня гуглил указатели, malloc и копировал.
Спасибо!
ЗДЕСЬ МОЙ СОБСТВЕННЫЙ ОТВЕТ
Теперь у меня есть рабочее решение. Меня беспокоит только то, что в нем могут быть утечки памяти.
Использование realloc() работает, и мне также нужно быть осторожным, чтобы убедиться, что я освобождаю () только указатели, которые я инициализировал с помощью malloc или realloc, а не указатели, инициализированные с помощью double * oldarray;
,
Рабочая версия выглядит так:
double * olddiagonal = (double *) malloc(sizeof(double));
olddiagonal[0] = otherfunction(otherstuff);
int iter = 1;
// A bunch of other stuff
while (<error tolerance condition>) {
double * new diagonal = (double *) malloc((iter+1)*sizeof(double));
newdiagonal[0] = otherfunction(moreotherstuff);
// Then I do a bunch of things and fill in the values of new diagonal using
// its other values and the values in olddiagonal.
// To finish, I free the old stuff, and use realloc to point old to new.
free(olddiagonal);
olddiagonal = (double *) realloc(newdiagonal, sizeof(double) * (iter+1));
iter++
}
Кажется, это работает для моих целей. Мое единственное беспокойство - возможные утечки памяти, но пока, это ведет себя хорошо и получает правильные значения.
1 ответ
Вот несколько объяснений:
double ** oldarray;
oldarray = &malloc(1*sizeof(double));
неправильно, потому что вы не храните результат malloc()
в любом месте, и поскольку он нигде не хранится, вы не можете взять его адрес. Вы можете получить эффект, который, по-видимому, имеете в виду, добавив промежуточную переменную:
double* intermediatePointer;
double** oldarray = &intermediatePointer;
intermediatePointer = malloc(1*sizeof(*intermediatePointer);
oldarray
теперь указатель на ячейку памяти intermediatePointer
, который указывает на выделенный шлепок памяти по очереди.
*oldarray = (double *) malloc(1*sizeof(double));
неправильно, потому что вы разыменовываете унифицированный указатель. Когда вы объявляете oldarray
с double** oldarray;
Вы резервируете память только для одного указателя, а не для чего-либо, на что должен указывать указатель (резервирование памяти не зависит от того, на что указывает указатель!). Значение, которое вы найдете в этой переменной указателя, не определено, поэтому вы абсолютно не можете контролировать, какой адрес памяти вы записываете, когда назначаете что-либо *oldarray
,
Всякий раз, когда вы объявляете указатель, вы должны инициализировать указатель, прежде чем разыменовать его:
int* foo;
*foo = 7; //This is always a bug.
int bar;
int* baz = &bar; //Make sure the pointer points to something sensible.
*baz = 7; //OK.
Ваш код ответа действительно правильный. Тем не менее, это может быть улучшено в отношении стиля:
Сочетание
int iter = 1;
while (<error tolerance condition>) {
...
iter++
}
призывает к использованию for()
цикл, который инкапсулирует определение и приращение переменной цикла в оператор управления циклом:
for(int iter = 1; <error tolerance condition>; iter++) {
...
}
В C приведение возвращаемого значения malloc()
совершенно излишне, это только загромождает ваш код. (Обратите внимание, что C++ не позволяет неявное преобразование void*
S как C, так int *foo = malloc(sizeof(*foo))
совершенно допустимый C, но не законный в C++. Однако в C++ вы бы не использовали malloc()
на первом месте.)