Определение поведения realloc () перед вызовом - PullRequest
6 голосов
/ 19 октября 2008

Насколько я понимаю, при запросе зарезервировать больший блок памяти функция 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 ()? Если да, то можно ли достичь кроссплатформенным способом?

Ответы [ 8 ]

7 голосов
/ 19 октября 2008
Поведение

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

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

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

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

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

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

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

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

1 голос
/ 28 октября 2008

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

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

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

0 голосов
/ 07 октября 2010

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

0 голосов
/ 28 октября 2008

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

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

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

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

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

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

0 голосов
/ 19 октября 2008

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

0 голосов
/ 19 октября 2008

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

0 голосов
/ 19 октября 2008

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...