Определение поведения функции realloc () перед ее вызовом

Насколько я понимаю, когда вас просят зарезервировать больший блок памяти, функция realloc () будет делать одно из трех разных действий:

if free contiguous block exists
    grow current block
else if sufficient memory
    allocate new memory
    copy old memory to new
    free old memory
else
    return null

Увеличение текущего блока - очень дешевая операция, поэтому я хотел бы воспользоваться этим поведением. Однако, если я перераспределяю память, потому что хочу (например) вставить символ в начало существующей строки, я не хочу, чтобы realloc () копировал память. В конечном итоге я скопирую всю строку с помощью realloc (), а затем снова скопирую ее вручную, чтобы освободить первый элемент массива.

Можно ли определить, что будет делать realloc ()? Если да, можно ли добиться кроссплатформенности?

Последний из трех случаев неверен, realloc вернет нулевой указатель, если памяти недостаточно, а не указатель, который вы ему передали.

Robert Gamble 19.10.2008 18:19

Как сказал Роберт Гэмбл, realloc () возвращает NULL при отсутствии памяти.

Jonathan Leffler 19.10.2008 18:43

Я полагаю, было бы ужасно придирчиво предполагать, что первым условием должно быть «если существует свободный непрерывный блок и объединенный блок достаточно велик» или что-то подобное ...

Jonathan Leffler 24.10.2008 09:57
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
3
1 958
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Нет - и если подумать, это не сработает. Между тем, как вы проверяете, что он собирается делать, и фактически выполняя это, другой процесс может выделить память. В многопоточном приложении это не сработает. Между тем, как вы проверяете, что он собирается делать, и фактически выполняя это, другой поток может выделить память.

Если вас беспокоят подобные вещи, возможно, пришло время взглянуть на структуры данных, которые вы используете, чтобы увидеть, можете ли вы там решить проблему. В зависимости от того, как построены эти строки, вы можете сделать это довольно эффективно с помощью хорошо спроектированного буфера.

Другой процесс разместит его в своем адресном пространстве, поэтому он не имеет отношения к делу.

Ilya 19.10.2008 17:41

Я не думаю, что это возможно кроссплатформенным способом. Здесь - это код для реализации ulibc, который может дать вам подсказку, как это сделать в зависимости от платформы, на самом деле лучше найти исходный код glibc, но этот был поверх поиска Google :)

Ответ принят как подходящий

Поведение realloc(), вероятно, зависит от его конкретной реализации. И основывать свой код на этом было бы ужасным взломом, который, мягко говоря, нарушает инкапсуляцию.

Лучшее решение для вашего конкретного примера:

  1. Найдите размер текущего буфера
    • Выделите новый буфер (с malloc()), больший, чем предыдущий
    • Скопируйте нужный префикс в новый буфер
    • Скопируйте строку из предыдущего буфера в новый буфер, начиная с префикса
    • Освободить предыдущий буфер

Это просто нелогично, почему бы не использовать перераспределение и не улучшить производительность, по крайней мере, в некоторых случаях, ваше предложение всегда будет memcpy. Он хочет улучшить производительность и избежать использования memcpy, когда он не нужен.

Ilya 19.10.2008 18:56

На самом деле это решение, которое я реализовал в настоящее время (хотя я каждый раз увеличиваю доступность, чтобы уменьшить количество перераспределений в будущем). Как говорит Илья, я искал более оптимальное решение, но, как вы отметили, я, вероятно, уже нашел лучший компромисс.

Ant 19.10.2008 19:01

Если вы хотите вставить de char в начале, вам придется использовать memmov () после перераспределения буфера ... Если realloc () не может увеличить текущий блок, вы скопируете память дважды, после того, как realloc (), как только решение memmov () Romulo всегда копирует память, но только один раз ...;)

alcuadrado 28.10.2008 09:27

Обратите внимание, что memmove, вероятно, на много медленнее, чем memcpy, особенно когда смещение составляет 1 байт, поэтому всегда лучше выделить новый буфер, в котором вы можете безопасно использовать memcpy.

R.. GitHub STOP HELPING ICE 08.08.2010 08:51

Проблема в том, что вам нужно копировать память, поэтому ваша система работает все медленнее и медленнее по мере того, как выделяемая сумма становится все больше и больше.

vy32 07.10.2010 07:04

Если препятствия хорошо подходят для ваших потребностей в распределении памяти, вы можете использовать их функциональность быстрорастущий. Obstacks - это особенность glibc, но они также доступны в библиотеке свобода, которая довольно переносима.

Как отмечено в комментариях, случай 3 в вопросе (нет памяти) неверен; realloc() вернет NULL, если нет доступной памяти [вопрос решен].

Стив МакКоннелл в «Код завершен» указывает, что если вы сохраните возвращаемое значение из realloc() в единственной копии исходного указателя, когда realloc() не работает, вы просто потеряете память. То есть:

void *ptr = malloc(1024);
...
if ((ptr = realloc(ptr, 2048)) == 0)
{
    /* Oops - cannot free original memory allocation any more! */
}

Различные реализации realloc () будут вести себя по-разному. Единственная безопасная вещь, которую можно предположить, - это то, что данные будут перемещены в всегда - что вы всегда будете получать новый адрес при использовании realloc () памяти.

Как заметил кто-то другой, если вас это беспокоит, возможно, пришло время взглянуть на ваши алгоритмы.

Помогло бы хранение вашей строки в обратном направлении?

Иначе... просто malloc () больше места, чем вам нужно, и когда у вас закончится место, скопируйте в новый буфер. Простая техника - каждый раз удваивать пространство; это работает очень хорошо, потому что чем больше строка (т.е. чем больше времени потребуется на копирование в новый буфер), тем реже это потребуется.

Используя этот метод, вы также можете выровнять строку в буфере по правому краю, чтобы можно было легко добавлять символы в начало.

Почему бы не оставить пустое буферное пространство слева от строки, например:

char* buf = malloc(1024);
char* start = buf + 1024 - 3;
start[0]='t';
start[1]='o';
start[2]='\0';

Чтобы добавить «on» в начало вашей строки, чтобы сделать ее «на \ 0»:

start-=2;
if (start < buf) 
  DO_MEMORY_STUFF(start, buf);//time to reallocate!
start[0]='o';
start[1]='n';

Таким образом, вам не придется копировать буфер каждый раз, когда вы захотите выполнить вставку в начале.

Если вам нужно делать вставки как в начале, так и в конце, просто выделите немного места на обоих концах; вставки в середине, очевидно, все равно потребуют от вас перетасовки элементов.

Лучше использовать связанный список. Выделите каждый из ваших объектов данных на странице, выделите другую страницу и получите ссылку на нее либо с предыдущей страницы, либо со страницы индекса. Таким образом, вы знаете, когда произойдет сбой следующего выделения, и вам никогда не придется копировать память.

Другие вопросы по теме