2015-11-14 2 views
24

Из того, что написано here, new выделяет в свободном магазине в то время как malloc использует кучи и оба термина часто означают то же самое.Безопасно ли выделение памяти realloc новыми?

С того, что написано here, realloc может переместить блок памяти в новое место. Если свободный магазин и куча - это два разных пространства памяти, значит ли это какая-то проблема?

В частности, я хотел бы знать, если это безопасно использовать

int* data = new int[3]; 
// ... 
int* mydata = (int*)realloc(data,6*sizeof(int)); 

Если нет, есть ли другой способ realloc памяти выделяется new безопасно? Я мог бы выделить новую область и memcpy содержание, но из того, что я понимаю, realloc может использовать ту же самую область, если это возможно.

+13

Просто используйте «вектор». –

+8

@KarolyHorvath Как вы знаете, что это жизнеспособный подход в каждом случае? Как насчет развертывания встроенной системы без поддержки стандартной библиотеки? Как насчет интеграции с C-интерфейсом, который может выполнять realloc? –

+6

@KarolyHorvath вы можете проверить, сколько времени занимает выделение 200 МБ памяти с помощью 'malloc' (несколько микросекунд) против' std :: vector' (~ 200 миллисекунд!). 'std :: vector' не является волшебным решением для каждой проблемы с памятью. –

ответ

39

Вы можете только realloc то, что было выделено через malloc (или семье, например calloc).

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

Это вероятно, но отнюдь не гарантировано, что C++ new и C malloc используют те же основные аллокатора, в этом случае realloc может работать для обоих. Но формально это в УБ-земле. И на практике это просто бесполезно рискованно.


C++ не предоставляет функциональные возможности, соответствующие realloc.

Ближайшим является автоматическое перераспределение (внутренних буферов) таких контейнеров, как std::vector.

Контейнеры C++ страдают от того, что они сконструированы таким образом, чтобы исключить использование realloc.


Вместо представленного кода

int* data = new int[3]; 
//... 
int* mydata = (int*)realloc(data,6*sizeof(int)); 

& hellip; сделать это:

vector<int> data(3); 
//... 
data.resize(6); 

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

В противном случае, если вы абсолютно необходимо общую эффективность realloc, но не принуждают принимать new, то вы можете использовать malloc и realloc. Используя интеллектуальные указатели, вы можете получить такую ​​же безопасность, как и в контейнерах C++.

+1

фрагмент, который вы написали, является самым идиоматическим способом перераспределения памяти на C++, но это верный путь чтобы убить вашу работу, если вы находитесь в этом поле. –

+2

@KyleStrand: Если вам нужно принять 'новый' для первоначального распределения, то ваш единственный ресурс для эффективности - использовать средства, специфичные для компилятора. Например. знание, что 'realloc' является безопасным с этим компилятором. В противном случае вы можете использовать интеллектуальные указатели с 'malloc' и' realloc'. Во всяком случае, помните первое (и второе) правило оптимизации, а именно: ** MEASURE **. –

+0

Спасибо. Я удалил свой комментарий, потому что ваш ответ ответил на мой вопрос. –

5

Это не безопасно, и это не изящно.

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

+4

Я не уверен, что нелепо говорит о realloc. –

+0

, используя новый/удалить с помощью realloc, путем переопределения или других способов заставить его работать, не изящно, пожалуйста, прочитайте эту тему. –

+0

Итак, вы имеете в виду это * потому что * это небезопасно, нецелесообразно пытаться * сделать это безопасным? Это не ясно из вашего ответа. И не думайте, что мне почему-то удалось оставить комментарий к вашему ответу без «чтения темы»; это бессмысленно оскорбительно. –

5

Да - если new фактически называется malloc (например, это работает VC++ new).

В противном случае. обратите внимание, что как только вы решите перераспределить память (потому что new называется malloc), ваш код больше компилятор, а не переносимый между компиляторами.

(Я знаю, что этот ответ может расстроить многих разработчиков, но я отвечаю, зависит от реальных фактов, а не только от идиоматики).

+0

Это верно для 'operator new []()', что и используется здесь, а не просто 'operator new()'? –

+0

на VC++ все стандартные операторы 'new' в конце концов вызывают' malloc'. –

+0

Да, но я был бы удивлен, если результат 'operator new []' был таким же, как и значение, возвращаемое вызовом 'malloc', из-за хранения счета. И если это не так, вы не можете передать его 'realloc'. –

7

В общем, не делайте этого. Если вы используете определяемые пользователем типы с нетривиальной инициализацией, в случае освобождения от копирования-освобождения, деструктор ваших объектов не будет называться по realloc. Копирование конструктор не будет называться тоже при копировании. Это может привести к неопределенному поведению из-за неправильного использования времени жизни (см. C++ Standard §3.8 Срок службы объекта, [basic.life]).

1 Срок службы объекта - это свойство времени выполнения объекта. Говорят, что объект имеет нетривиальную инициализацию, если он имеет тип класса или агрегата, и он или один из его членов инициализируется конструктором, отличным от тривиального конструктора по умолчанию. [Примечание: инициализация тривиальным конструктором copy/move является нетривиальной инициализацией. -end примечание]

Время жизни объекта типа Т начинается, когда:

- хранение с правильным выравниванием и размером для типа Т получается, а

- если объект имеет нетривиальные инициализация, его инициализация завершена.

Время жизни объекта типа Т заканчивается, когда:

- если Т представляет собой тип класса с нетривиальным деструктора (12.4), начинается деструктор вызова или

- хранения, который объект занимает повторно или освобождается.

И позже (курсив мой):

3 Свойства, приписываемые объектам на протяжении всего этого международного стандарта применяются для данного объекта только в течение его срока службы.

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

4

Это небезопасно. Во-первых, указатель, который вы передаете realloc, должен быть получен от malloc или realloc: http://en.cppreference.com/w/cpp/memory/c/realloc.

Во-вторых, результат new int [3] не должен совпадать с результатом функции распределения - дополнительное пространство может быть выделено для хранения количества элементов.

(А для более сложных типов, чем int, realloc не будет в безопасности, так как он не требует копирования или перемещения конструкторами.)

3

Вы можете быть в состоянии (не во всех случаях), но вы не должны «т. Если вам нужно изменить размер таблицы данных, вместо этого вы должны использовать std::vector.

Информация о том, как его использовать, указана в других статьях SO question.

12

только возможно, соответствующее ограничение C++ добавляет к realloc является то, что C++ с malloc/calloc/realloc не должны быть реализованы в терминах ::operator new, и ее free не должны быть реализованы в терминах ::operator delete (на С ++ 14 [с. таНос] p3-4).

Это означает, что гарантия, которую вы ищете, не существует на C++. Это также означает, однако, что вы можете реализовать ::operator new с точки зрения malloc. И если вы это сделаете, то теоретически результат ::operator new может быть передан в realloc.

На практике вы должны быть обеспокоены тем, что результат new не соответствует результату ::operator new. Компиляторы C++ могут, например, объединить несколько выражений new, чтобы использовать один звонок ::operator new. Это то, что компиляторы уже делали, когда стандарт не позволял этого, IIRC и стандарт теперь позволяют это (на C++ 14 [expr.new] p10). Это означает, что даже если вы идете по этому маршруту, у вас все еще нет гарантии, что передача указателей new в realloc делает что-либо значимое, даже если это уже не неопределенное поведение.

+1

Пожалуйста, добавьте ссылки для (1)« Malloc/calloc/realloc не должен реализовываться в терминах :: operator new ", а для (2) о практике, еще не одобренной стандартом, что« компиляторы C++ могут, например, комбинировать несколько новых выражений для использования одного вызова one :: operator new call ». –

+0

@ Cheersandhth.-Alf Добавлена ​​ссылка для первой. Не включили фактический стандартный текст, потому что это не вопрос [язык-юрист]. У меня нет примера, готового к нескольким «новым» вызовам, которые дают результаты, которые я описываю, а быстрый и простой пример, который просто удаляет выделенную память, не объединяет распределения в один, он просто оптимизирует распределение полностью. – hvd

0

Эти функции в основном используется в С.

MemSet устанавливает байты в блоке памяти на конкретное значение.

malloc выделяет блок памяти.

calloc, то же, что и malloc. Единственное отличие состоит в том, что он инициализирует байты до нуля.

В C++ предпочтительным методом выделения памяти является использование нового.

C: int intArray = (int *) malloc (10 * sizeof (int)); C++: int intArray = new int [10];

C: int intArray = (int *) calloc (10 * sizeof (int)); C++: int intArray = new int10;

+2

Я не верю, что это отвечает на вопрос, потому что он не рассматривает перераспределение вообще. –

3

В общем, нет.

Есть убивание вещей, которые должны иметь, чтобы сделать его безопасным:

  1. побитового копирования типа и отказ источника должна быть безопасными.
  2. Деструктор должен быть тривиальным, или вы должны на месте уничтожить элементы, которые хотите освободить.
  3. Либо конструктор тривиален, либо вы должны создавать новые элементы на месте.

Тривиальные типы удовлетворяют вышеуказанным требованиям.

В дополнении:

  1. new[] -функция должна передать запрос на к malloc без каких-либо изменений, и не делать какую-либо бухгалтерию на стороне. Вы можете заставить это, заменив global new [] и delete [] или те, что находятся в соответствующих классах.
  2. Компилятор не должен запрашивать больше памяти, чтобы сохранить количество выделенных элементов или что-то еще.
    Невозможно заставить это, хотя компилятор не должен сохранять такую ​​информацию, если тип имеет тривиальный деструктор как вопрос Качество выполнения.
Смежные вопросы