Я хочу использовать realloc для увеличения объема памяти при сохранении указателя без изменений (потому что вызывающие его используют). realloc не всегда делает это; иногда он возвращает другой указатель и освобождает старый. Я хотел бы «попытаться» перераспределить память и, если это невозможно, использовать другой метод, используя оригинальный указатель — но realloc уже уничтожил это!
Есть ли способ попытаться увеличить объем памяти malloc, не уничтожая (как это делает realloc) старый указатель, если это невозможно?
Например.
void *pold;
void *pnew = realloc(pold, newsize);
if (pnew != pold)
{
free(pnew);
DoDifferently(pold); // but pold is freed already
}
Постскриптум Меня не волнует переносимость (только linux, таким образом, тег).
Вам следует взглянуть на исходный код realloc () из используемой вами библиотеки libc. Оттуда должно быть легко увидеть путь, по которому можно увеличить размер, а в другом случае вместо него будет возвращен новый указатель. Затем используйте это для кодирования вашей собственной функции tryrealloc ().
Например, это исходный код realloc () из uclibc: http://cristi.indefero.net/p/uClibc-cristi/source/tree/nptl/libc/stdlib/malloc/realloc.c
24 void *
25 realloc (void *mem, size_t new_size)
26 {
...
57 if (new_size > size)
58 /* Grow the block. */
59 {
60 size_t extra = new_size - size;
61
62 __heap_lock (&__malloc_heap_lock);
63 extra = __heap_alloc_at (&__malloc_heap, base_mem + size, extra);
64 __heap_unlock (&__malloc_heap_lock);
65
66 if (extra)
67 /* Record the changed size. */
68 MALLOC_SET_SIZE (base_mem, size + extra);
69 else
70 /* Our attempts to extend MEM in place failed, just
71 allocate-and-copy. */
72 {
73 void *new_mem = malloc (new_size - MALLOC_HEADER_SIZE);
74 if (new_mem)
75 {
76 memcpy (new_mem, mem, size - MALLOC_HEADER_SIZE);
77 free (mem);
78 }
79 mem = new_mem;
80 }
81 }
...
Я удалил некоторые части для ясности. Но вы можете видеть, что в строке 66 он проверяет, может ли он просто увеличить память для текущего указателя. Это та часть, которую вы хотите сохранить. else
регистр, начинающийся со строки 69, предназначен для обработки случая, когда старая память будет освобождена и будет возвращен новый указатель. Это та часть, которую вы хотите выпустить и обращаться с ней по-другому. Из того, что вы говорите, я думаю, что вы захотите удалить только строку 77, где она делает бесплатно.
Если вы пойдете этим путем, помните, что вам придется вручную освободить старый или новый указатель, так как оба теперь будут действительны (и вам не нужна утечка памяти).
Кроме того, это для uclibc. Если вы уже используете другой libc, вы должны основать свой новый tryrealloc()
функция на realloc()
функция этого libc.
РЕДАКТИРОВАТЬ: Вы должны быть осторожны, если вы используете этот подход. Вы будете основывать свое решение на внутренних элементах диспетчера памяти, поэтому вещи могут меняться и различаться в разных реализациях libc, но также в разных версиях одного и того же libc. Так что делайте это с надлежащей осторожностью и предупреждением.
Нет переносимого решения этой проблемы, и непереносимое решение не свободно от риска.
Непереносимое решение, которое работает с GNU malloc
это использовать malloc_usable_size
чтобы выяснить, насколько велика область памяти на самом деле. Однако, даже если область памяти достаточно велика, я не уверен, что realloc
не гарантировано использовать его. IIRC, раньше был вариант, который вызвал realloc
всегда выделять новую память, но я больше не могу ее найти; Я выглядел не очень пристально, хотя.
Я не думаю, что есть портативный realloc
-типа, которая сделает это
Одним из относительно простых переносимых решений является предварительное выделение большего блока памяти, чем вам нужно изначально. Это позволит некоторый рост без изменения указателя (по сути, вы будете делать свои собственные на месте realloc
).
Как только вы превысите исходный блок, вам придется выделить новый вместо него. Это ваш сценарий «провала».
Насколько я знаю, такая вещь невозможна (используется стандартная библиотека для переносимости). Тем не менее, есть простые обходные пути. Поскольку ваши абоненты используют этот указатель, вы должны исправить это для них. Одним из решений является следующее:
void *do_something(void *your_pointer)
{
void *new_pointer;
new_pointer = realloc(your_pointer, ...);
/* more logic */
return new_pointer;
}
и скажи им, чтобы использовать do_something
как это:
new_pointer = do_something(my_pointer);
if (new_pointer) /* if returning NULL is at all possible */
my_pointer = new_pointer;
Кроме того, вы можете позаботиться об этом самостоятельно:
int do_something(void **pointer_to_your_pointer)
{
void *new_pointer;
new_pointer = realloc(*pointer_to_your_pointer, ...);
/* more logic */
*pointer_to_your_pointer = new_pointer;
return error_code;
}
и скажи им, чтобы он использовал это так:
do_something(&my_pointer);
Альтернативная библиотека управления памятью jemalloc предоставляет такую функциональность. С xallocx()
это можно сделать так:
size_t realsize = xallocx(pold, newsize, 0, 0);
if (realsize < newsize)
{
DoDifferently(pold);
}
Почему не просто malloc
максимальная сумма, которая вам может понадобиться? Память обычно выделяется только после ее использования. Для тонкой настройки вы можете использовать mmap
выделить область памяти.
это не в руках программистов reallocate
тот же острый memory region
.. Вы можете просто увеличить size
с помощью realloc()
.. вы можете или не можете получить то же самое memory location
после использования realloc
.. скорее всего, вы не можете .. не может быть никаких проблем, если есть изменение в памяти ..