Подтвердить что ты не робот

Безопасно ли перераспределять память, выделенную с помощью нового?

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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


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

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

Контейнеры С++ страдают от разработки таким образом, который исключает использование 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. Используя интеллектуальные указатели, вы можете получить большую часть той же безопасности, что и в контейнерах С++.

Ответ 2

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

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

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

Ответ 3

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

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

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

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

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

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

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

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

И позже (внимание мое):

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

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

Ответ 4

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

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

Ответ 5

Да - если new на самом деле называется malloc в первую очередь (например, так работает VС++ new).

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

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

Ответ 6

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

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

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

Ответ 7

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

Сведения о том, как его использовать, перечислены в другом SO-вопросе.

Ответ 8

В общем, нет.

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

  • Побитовое копирование типа и отказ от источника должны быть безопасными.
  • Деструктор должен быть тривиальным, или вы должны на месте - уничтожить элементы, которые хотите освободить.
  • Любой конструктор тривиален, или вы должны создавать на месте новые элементы.

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

Кроме того:

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

Ответ 9

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

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

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

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

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

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

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