911 Динамическое распределение памяти
Здесь указан адрес переменной в формате [сегмент]:[смещение]. Полный адрес переменной равен 8FAC0+FFF4=9FAB4
- Для переменной ptri, описанной как
int i = 4, *ptri;
ptri = &i;
окно просмотра имеет вид
8FAC:FFF2 |
п |
Ds:FFF4 |
[0] |
4(0x0004) | |
Int * |
Здесь указаны: адрес самой переменной ptri, равный 8FAC:FFF2; значение этой переменной Ds:FFF4; а также содержимое ячейки по адресу Ds:FFF4, т.е. значение i. Для того, чтобы узнать содержимое ячеек, окружающих переменную i, нужно воспользоваться комбинацией клавиш Alt-I, ввести начальный индекс (Starting index) и число ячеек (Count). Если, например, введены числа -5 и 15, то можно в приведенном выше окне можно просмотреть элементы массива ptri[-5], ptri[-4],…,ptri[10].
Объекты, размещенные в куче, не имеют имени. Поэтому просматривать динамические объекты можно только предыдущим способом, т.е. по адресу.
Абсолютные адреса и размеры областей памяти в модели large
Область кода занимает часть ОЗУ, содержащую параграфы с номерами от CS до DS-1. Таким образом, размер области кода равен (DS-CS)*0x10.
Область данных занимает часть ОЗУ, содержащую параграфы с номерами от DS SS-1. Таким образом, размер области данных равен (SS-DS)*0x10.
Стек начинается с параграфа SS, но растет справа налево, от старших адресов к младшим. Новые данные помещаются в стек в вершине стека. Смещение вершины стека относительно SS равно SP. Таким образом, полный адрес вершины равен SS:SP. В начале программы стек пуст, поэтому он занимает ячейки с адресами от SS*0x10 до SS*0x10, а размер стека равен SP.
На кучу остается память с адреса SS*0x10+SP по 0xA0000=640K.
Реальный размер кучи зависит от способа запуска программы. Сама оболочка Borland C++2.0 занимает в ОЗУ порядка 200K, и для программы, выполнямой из оболочки, остается немного места - примерно 300K. Если же программа запускается из командной строки DOS или из Norton Commander, то куча будет значительно больше.
При выполнении программы из оболочки и в отладчике под кучу отводится еще меньше места, поэтому ее размер в этом случае разрешается устанавливать вручную с помощью опции Options-Debugger-Program heap size.
2. Основные функции ДРП
Выделение памяти
void *malloc(size_t size);
Выделяет блок ДП размером size байт. Здесь size_t совпадает с типом unsigned int. Таким образом, блок не превосходит размер в 64K. В случае отсутствия непрерывного блока заказанной длины, возвращается NULL.
Пример 1. Выделение массива для 1000 чисел типа float.
main() {
float *ptrf;
if((ptrf =(float *) malloc(1000 * sizeof(float)) == NULL)
printf("\nОшибка при выделении памяти!");
}
Освобождение памяти
void free(void *block);
Освобождает блок ДП, начинающийся с адреса block. Этот адрес должен находится в заголовке ранее выделенного блока (см. п.3). В противном случае, будет освобожден случайный блок и возникнет логическая ошибка. Динамические переменные не освобождаются автоматически при выходе из области действия и, таким образом, засоряют кучу.
Перевыделение памяти
void *realloc(void *block, size_t size);
Изменяет размер блока, на который указывает block. Новый размер блока будет равен size. Старая информация сохраняется. При нехватке свободных байт блок будет перемещен в новое место с одновременным копированием информации. Функция возвращает адрес нового блока, не изменяя переменную block.
Пример 2. Выделение памяти под двумерный массив
main() {
float **A;
int n=5, n=10;
A = (float **)malloc(m * sizeof(float *));
if(A == NULL)
{
printf("\nОшибка при выделении памяти под массив указателей!");
exit(1);
}
for (int i=0; i< m; i++)
{
A[i] = (float *)malloc(n * sizeof(float));
if(A[i] == NULL)
{
printf("\nОшибка памяти под%d-ую строку!", i);
for(int j=0; j< i; j++)
free(A[j]);
free(A);
exit(2);
}
//…….
for(i=0; i< m; i++)
free(A[i]);
free(A);
}
3. Менеджер ДРП
Управлением ДП занимается специальный фрагмент кода, вызываемый в функциях ДРП. Он называется менеджером ДП. Мы исследуем работу менеджера в модели памяти large. В других моделях менеджер устроен по-другому.
Первоначально менеджер рассматривает кучу как свободную область. При каждом обращении к памяти выделяется непрерывный блок, состоящий из одного или нескольких параграфов. В первом параграфе имеется заголовок блока. При освобождении памяти блок помечается как свободный. Таким образом, в процессе работы программы свободные и занятые блоки начинают чередоваться. В результате увеличивается фрагментация кучи, когда общей свободной памяти много, а непрерывного блока необходимой длины нет.
Выделяемый блок памяти имеет вид
2 |
2 |
14 |
16 |
… |
16 |
В заголовке находятся:
¾ длина блока в параграфах (2 байта),
¾ сегментный адрес предыдущего блока (2 байта),
¾ далее идут данные. Адрес начала данных и возвращается функциями ДРП.
Блоки организованы в односвязный список. При запуске программы в начале куче выделяется блок для системных нужд. Первый выделенный программой блок будет ссылаться на имеющийся блок и т.д. При освобождении блока в его заголовке обнуляется ссылка на предыдущий блок. Длина блока не изменяется, а в информационную область заносятся данные, используемые при корректировке кучи.
Таким образом, зная адрес последнего занятого блока, можно определить длину, адрес и статус остальных блоков. Рассмотрим программу
main(){
char *block1=(char *)malloc(100);
char *block2=(char *)malloc(110);
char *block3=(char *)malloc(120);
free(block2);
}
Выполняя ее в отладчике, увидим, что до освобождения второго блока куча будет иметь вид (конкретные адреса будут другими!)
0х0007 |
0х90EF |
… |
0x0008 |
0x910F |
…. |
0x0008 |
0x9116 |
Другие рефераты на тему «Программирование, компьютеры и кибернетика»:
Поиск рефератов
Последние рефераты раздела
- Основные этапы объектно-ориентированного проектирования
- Основные структуры языка Java
- Основные принципы разработки графического пользовательского интерфейса
- Основы дискретной математики
- Программное обеспечение системы принятия решений адаптивного робота
- Программное обеспечение
- Проблемы сохранности информации в процессе предпринимательской деятельности