Выделение/Освобождение Памяти? [закрытый]
в последнее время я смотрю на распределение памяти, и я немного смущен основами. Я не смог разобраться в простых вещах. Что значит выделять память? Что происходит? Я был бы признателен за ответы на любой из этих вопросов:
- где находится" память", которая выделяется?
- что это за "память"? Пространство в массиве? Или что-то еще?
- что происходит именно тогда, когда эта "память" выделяется?
- что происходит, когда память освобождается?
-
это также очень помогло бы мне, если бы кто-то мог ответить, что делает malloc в этих строках c++:
char* x; x = (char*) malloc (8);
спасибо.
4 ответов
Модель Памяти
стандарт C++ имеет модель. Он пытается моделировать память в компьютерной системе в общем виде. Стандарт определяет, что байт-это единица хранения данных в модели памяти и что память состоит из байтов (§1.7):
основной единицей хранения в модели памяти C++ является байт. [...] Память, доступная программе на C++, состоит из одной или нескольких последовательностей непрерывных байты.
Объектная Модель
стандарт всегда обеспечивает объектная модель. Это указывает, что объект является областью хранения (поэтому он состоит из байтов и находится в памяти) (§1.8):
конструкции в программе C++ создают, уничтожают, ссылаются на, получают доступ и манипулируют объектами. Объект-это область хранения.
Итак, поехали. Память-это место, где хранятся объекты. Для хранения объект в памяти, должна быть выделена необходимая область хранения.
функции распределения и освобождения
стандарт предоставляет две неявно объявленные глобальные функции распределения области:
void* operator new(std::size_t);
void* operator new[](std::size_t);
как они реализованы, не является проблемой стандарта. Все, что имеет значение, - это то, что они должны вернуть указатель на некоторую область хранения с количеством байтов, соответствующим переданному аргументу (§3.7.4.1):
функция распределения пытается выделить запрашиваемый объем хранилища. В случае успеха он возвращает адрес начала блока памяти, длина которого в байтах должна быть не менее запрошенного размера. Нет никаких ограничений на содержимое памяти при возврате из функции распределения.
он также определяет две соответствующие функции освобождения:
void operator delete(void*);
void operator delete[](void*);
, которые определены для освобождения ранее выделенное хранилище (§3.7.4.2):
если аргумент, заданный функции освобождения в стандартной библиотеке, является указателем, который не является нулевым значением указателя (4.10), функция освобождения освобождает хранилище, на которое ссылается указатель, делая недействительными все указатели, ссылающиеся на любую часть освобожденного хранилища.
new
и delete
как правило, вам не нужно использовать функции распределения и освобождения непосредственно, потому что они дают вам только неинициализированную память. Вместо этого в C++, вы должны использовать new
и delete
для динамического выделения объектов. А новый-выражение получает хранилище для запрошенного типа, используя одну из вышеуказанных функций распределения, а затем инициализирует этот объект каким-либо образом. Например new int()
выделит место для int
объект, а затем инициализировать его в 0. См. §5.3.4:
A new-expression получает хранилище для объекта путем вызова функции распределения (3.7.4.1).
[...]
A новый-выражение который создает объект типа T инициализирует этот объект [...]
в противоположном направлении, delete
вызовет деструктор объекта (если таковой имеется), а затем освободит хранилище (§5.3.5):
если значение операнда удалить-выражение не является нулевым значением указателя,удалить-выражение вызовет деструктор (если есть) для объекта или элементов удаляемого массива.
[...]
если значение операнда удалить-выражение не является нулевым значением указателя,удалить-выражение вызовет функцию освобождения (3.7.4.2).
Другие Отчисления
однако, это не единственные способы выделения или освобождения хранилища. Многие конструкции языка неявно требуют выделения хранилища. Например, предоставление определения объекта, например int a;
, также требует хранения (§7):
определение заставляет зарезервировать соответствующий объем хранилища и выполнить любую соответствующую инициализацию (8.5).
C стандартная библиотека:malloc
и free
кроме того,<cstdlib>
заголовок приносит содержание stdlib.h
C стандартная библиотека, которая включает в себя malloc
и free
функции. Они также определяются стандартом C для выделения и освобождения памяти, подобно функциям выделения и освобождения, определенным стандартом c++. Вот определение malloc
(С99 §7.20.3.3):
void *malloc(size_t size);
описание
Themalloc
функция выделяет пространство для объекта, размер которого задаетсяsize
и чья ценность неопределенна.
возвращает
Themalloc
функция возвращает либо нулевой указатель, либо указатель на выделенное пространство.
и определение free
(С99 §7.20.3.2):
void free(void *ptr);
описание
Thefree
функция вызывает пространство, на которое указываетptr
быть освобожденным, то есть сделанным доступно для дальнейшего распределения. Еслиptr
- это указатель null, никаких действий не происходит. В противном случае, если аргумент не соответствует указателю, ранее возвращенномуcalloc
,malloc
илиrealloc
функция, или если пространство было освобождено вызовомfree
илиrealloc
, поведение не определено.
однако, никогда нет хорошего оправдания для использования malloc
и free
в C++. Как описано выше, C++ имеет свои альтернативы.
ответы на вопросы
так отвечаем на вопросы:
-
где находится" память", которая выделяется?
стандарт C++ не волнует. Он просто говорит, что программа имеет некоторую память, которая состоит из байтов. Эта память может быть выделена.
-
что это за "память"? Пространство в массиве? Или что-то еще?
что касается стандарта, памяти это просто последовательность байтов. Это целенаправленно очень общий, так как стандарт только пытается модель обычные компьютерные системы. Вы можете, по большей части, думать об этом как о модели ОЗУ вашего компьютера.
-
что происходит, когда эта "память" выделяется?
выделение памяти делает некоторую область хранения доступной для использования программой. Объекты инициализируются в выделенной памяти. Все, что вам нужно знать, что вы можете выделить память. Фактическое распределение физическая память для вашего процесса, как правило, выполняется операционной системой.
-
что происходит, когда память освобождается?
освобождение некоторой ранее выделенной памяти приводит к тому, что эта память недоступна для программы. Он становится освобожденным хранилищем.
-
это также очень помогло бы мне, если бы кто-то мог ответить, что делает malloc в этих строках c++:
char* x; x = (char*) malloc (8);
здесь
malloc
is просто выделяем 8 байт памяти. Указатель она возвращает брошена наchar*
и хранящиеся вx
.
1) где находится выделяемая "память"?
Это совершенно другое на основе вашей операционной системы, среды программирования (gcc vs Visual C++ vs Borland C++ vs anything else), компьютера, доступной памяти и т. д. В общем, память выделяется из того, что называется кучей, область памяти просто ждет вас, чтобы использовать. Как правило, он будет использовать вашу доступную ОЗУ. Но всегда есть исключения. По большей части, пока это дает нам память, откуда она берется, не так уж и важна. Существуют специальные типы памяти, такие как виртуальная память, которая может быть или не быть в оперативной памяти в любой момент времени и может быть перемещена на жесткий диск (или аналогичное устройство хранения), если у вас закончилась реальная память. Полное объяснение будет очень длинным!
2) Что такое "память"? Пространство в массиве? Или что-то еще?
память, как правило, ОЗУ в вашем компьютере. Если полезно думать о память как гигантский "массив", он работает как один, а затем подумайте об этом как тонна байтов (8-битные значения, как unsigned char
значения). Он начинается с индекса 0 в нижней части памяти. Однако, как и раньше, здесь есть тонны исключений, и некоторые части памяти могут быть сопоставлены с оборудованием или вообще не существовать!
3) что происходит, когда эта "память" выделяется?
в любой момент времени должно быть (мы очень надеемся!) некоторые из него доступно программное обеспечение для выделения. Как он выделяется сильно зависит от системы. В общем случае выделяется область памяти, распределитель помечает ее как используемую, а затем вам дается указатель на использование, который сообщает программе, где во всей памяти вашей системы находится эта память. В вашем примере программа найдет последовательный блок из 8 байтов (char) и вернет указатель на то, где он нашел этот блок после того, как он помечает его как "используется".
4) Что происходит когда именно освобождается память?
система отмечает, что память снова доступна для использования. Это невероятно сложно, потому что это часто вызывает дыры в памяти. Выделите 8 байтов, затем еще 8 байтов, затем освободите первые 8 байтов, и у вас есть отверстие. Есть целые книги, написанные по обработке освобождения, выделения памяти и т. д. Так что, надеюсь, короткого ответа будет достаточно!
5) это также помогло бы мне, если кто-то может ответить, что делает malloc в этих строках c++:
действительно грубо, и предполагая, что это в функции (кстати, никогда не делайте этого, потому что это не освобождает вашу память и вызывает утечку памяти):
void mysample() {
char *x; // 1
x = (char *) malloc(8); // 2
}
1) это указатель, зарезервированный в локальном пространстве стека. Он не был инициализирован, поэтому он указывает на то, что этот бит памяти имел в нем.
2) он вызывает malloc с параметром 8. Бросок просто дайте C / C++ знать, что вы намерены, чтобы он был (char *) потому что он возвращает (void*), что означает, что он не имеет примененного типа. Затем результирующий указатель хранится в переменной x.
в очень грубой 32-битной сборке x86 это будет выглядеть смутно как
PROC mysample:
; char *x;
x = DWord Ptr [ebp - 4]
enter 4, 0 ; Enter and preserve 4 bytes for use with
; x = (char *) malloc(8);
push 8 ; We're using 8 for Malloc
call malloc ; Call malloc to do it's thing
sub esp, 4 ; Correct the stack
mov x, eax ; Store the return value, which is in EAX, into x
leave
ret
фактическое распределение неопределенно описано в пункте 3. Malloc обычно просто вызывает системную функцию для этого, которая обрабатывает все остальное, и, как и все остальное здесь, она дико отличается от ОС к ОС, системы к системе и т. д.
1 . Где находится выделяемая "память"?
С точки зрения языка, это не указано, и в основном потому, что мелкие детали часто не имеет значения. Кроме того,C++
стандартные, как правило, свойственно ошибаться на стороне в соответствии с указанием сведений об оборудовании, чтобы минимизировать ненужные ограничения (как на платформах компиляторы могут работать на, и о возможных оптимизаций).
ответ sftrabbit дает отличный обзор этого конца вещей (и это все, что вам действительно нужно), но я могу привести несколько примеров, если это поможет.
Пример 1:
на достаточно старом однопользовательском компьютере (или достаточно маленьком встроенном) большая часть физической ОЗУ может быть непосредственно доступна вашей программе. В этом случае вызов malloc
или new
по существу является внутренним бухгалтерским учетом, позволяя библиотеке времени выполнения отслеживать, какие куски этой ОЗУ в настоящее время используются. Вы можете сделать это вручную, но это довольно быстро надоедает.
Пример 2:
в современной многозадачной операционной системе физическая ОЗУ используется совместно со многими процессами и другими задачами, включая потоки ядра. Он также используется для кэширования диска и буферизации ввода-вывода в фоновом режиме и дополняется подсистемой виртуальной памяти, которая может обмениваться данными на диск (или какое-либо другое устройство хранения), когда они не используются.
в этом случае, называя new
может сначала проверить, процесс уже имеет достаточно свободного места внутри, и запросить больше от ОС, если нет. Какая бы память ни возвращалась, она может быть физической или виртуальной (в этом случае физическая оперативная память не может быть назначена для ее хранения, пока она не будет фактически доступна). Вы даже не можете сказать разницу, по крайней мере, без использования специфичных для платформы API, потому что оборудование памяти и ядро сговорились скрыть ее от вас.
2 . Что это за "память"? Пространство в массиве? Или что-то еще?
в Примере 1 это что-то вроде пространства в массиве: возвращаемый адрес идентифицирует адресуемый кусок физической ОЗУ. Даже здесь адреса ОЗУ не обязательно являются плоскими или смежными - некоторые адреса могут быть зарезервированы для ПЗУ или портов ввода-вывода.
в Примере 2 это индекс во что-то более виртуальное: адресное пространство вашего процесса. Это абстракция, используемая для скрытия базовых сведений о виртуальной памяти из процесса. Когда вы получаете доступ этот адрес, аппаратное обеспечение памяти может напрямую обращаться к некоторой реальной оперативной памяти,или может потребоваться попросить подсистему виртуальной памяти предоставить некоторые.
3 . Что происходит, когда эта "память" выделяется?
в общем случае возвращается указатель, который вы можете использовать для хранения столько байтов, сколько вы просили. В обоих случаях malloc
или new
оператор сделает некоторое домашнее хозяйство для того чтобы отслеживать которые части адресного пространства вашего процесса использованы и которые оставаться свободными.
4 . Что происходит, когда память освобождается?
опять в общем,free
или delete
будет делать некоторую уборку, чтобы они знали, что память доступна для повторного выделения.
это также очень помогло бы мне, если бы кто-то мог ответить, что делает malloc в этих строках c++:
char* x;
x = (char*) malloc (8);
возвращает указатель, который является либо NULL
(если он не мог найти 8 байтов want) или какое-то ненулевое значение.
единственное, что вы можете с пользой сказать об этом ненулевом значении, это:
- законно (и безопасно) получить доступ к каждому из этих 8 байтов
x[0]..x[7]
, - это незаконно (неопределенное поведение) для доступа
x[-1]
илиx[8]
или на самом деле любойx[i]
если0 <= i <= 7
- легально сравнить любой
x, x+1, ..., x+8
(хотя вы не можете разыменования последние тех) - если ваша платформа / оборудование / что угодно имеют какие-либо ограничения на то, где вы можете хранить данные в памяти, то
x
отвечает
выделять память - значит запрашивать память у операционной системы. Это означает, что сама программа запрашивает "пространство" в ОЗУ только тогда, когда ей это нужно. Например, если вы хотите использовать массив, но не знаете его размер до запуска программы, вы можете сделать две вещи: - объявить и массив[x] с X, выделенным вами, произвольно длинным. Например, 100. Но как насчет того, что вашей программе просто нужен массив из 20 элементов? Ты зря тратишь память. - тогда вы можете запрограммировать malloc an массив элементов x только тогда, когда он знает правильный размер x. Программ в памяти разделены на 4 сегмента: - стек (необходим для вызова функций) -код (исполняемый код bibary ) - данные (глобальные переменные/данные) - куча, в этом сегменте вы найдете выделенную память. Когда вы решите, что вам больше не нужна выделенная память, вы вернете ее операционной системе.
Если вы хотите выделить и массив из 10 целых чисел, вы делаете:
инт *массив = (инт *)Танос(оператор sizeof(тип int) * 10)
и затем вы возвращаете его в ОС с помощью free (array)