4373

Управление памятью на уровне пользователя

Реферат

Информатика, кибернетика и программирование

Управление памятью на уровне пользователя На этапе постановки задачи программист должен решить, какую программу он должен создать, что и какими данными она должна оперировать. То есть возникает вопрос о количественной оценке используемых данных. Есл...

Русский

2012-11-18

129.5 KB

2 чел.

Управление памятью на уровне пользователя

На этапе постановки задачи программист должен решить, какую программу он должен создать, что и какими данными она должна оперировать. То есть возникает вопрос о количественной оценке используемых данных.

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

Например:

int a,b,c;            // объявление трех целочисленных переменных

float Arr[10];   // объявление массива вещественных данных,

                                // причем память выделяется под 10 элементов

Теперь мы можем обратиться к трем целочисленным переменным и десяти вещественным значениям, являющимся элементами массива.

Стандарт высокоуровневых языков программирования определяет правила работы с переменными – к переменной можно обратиться только в том случае, если она уже была объявлена, в противном случае обращение к ней невозможно (скорее всего, ее еще нет - память под нее не выделена).

При создании реальных программ на этапе проектирования мы можем определить лишь тип используемых данным. Дать точную количественную оценку объему используемых данных априори мы не в состоянии, однако выделить ее по максимуму из доступной статической памяти не целесообразно по двум основным причинам: во первых, физической памяти может не хватить, а во-вторых, вся выделенная память может и не понадобиться, но она будет выделена и загружена в адресное пространство, выделяемое для запуска пользовательских программ. Запущенные на компьютере программы конкурируют за машинные ресурсы. Под ресурсами в данном случае понимаем оперативную память и место на жестком диске, необходимые для работы программы. Машинные ресурсы ограничены, особенно остро это ограничение распространяется на оперативную память. Если мы какой-то программе отдадим то, что ей не нужно, то другим программам эти ресурсы становятся недоступны, сама же программа возвращать «захваченные» ресурсы не в состоянии.

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

За выделение памяти программам, формирование их адресного пространства отвечает специальная программа, называемая диспетчером (менеджером) памяти. Фактически она является тем слоем абстракции, который снимает с нас необходимость учитывать реальное адресное пространство компьютера. Для программиста важна лишь та модель памяти, которая используется его программой, а именно, какой максимально допустимый объем кода он может поместить в один модуль и какой максимальный объем данных может быть доступен его программе.

1. Адресное пространство программ на С/С++

Адресное пространство программ, написанных на языках С и С++ состоит из пяти различных областей памяти, называемых сегментами: сегмента кода, сегмента статических данных, сегмента кучи (heap), сегмента стека и области доступной оперативной памяти.

2. Выделение памяти в ANSI C

Четыре библиотечные функции образуют основу управления динамической памятью С. Библиотечные функции С, в свою очередь, обычно используются для реализации других выделяющих память библиотечных функций и операторов С++ new и delete.

2.1. Библиотечные вызовы: malloc(), calloc(), realloc(), free()

Динамическую память выделяют с помощью функций malloc() или calloc(). Эти функции возвращают указатели на выделенную память. Для изменения размера ранее выделенного блока памяти используется функция free(). Динамически выделенная память освобождается функцией free().

Прототипы функций:

void *calloc(size_t nmemb, size_t size);

void *malloc(size_t size);

void free(void *ptr);

void *realloc(void *ptr, size_t newSize);

Функции выделения памяти возвращают тип void *. Это бестиповый или общий указатель, все, что с ним можно сделать – это привести его к другому типу и назначить типизированному указателю.

Тип size_t является беззнаковым целым типом, используется для  представления размера блока памяти. Он используется для динамического выделения памяти. Для большинства систем size_t является unsigned long, но лучше явно использовать size_t вместо привычного unsigned.

Тип ptrdiff_t используется ля вычисления значения адреса в арифметике указателей.

#define MAXBUF 25

char *p;

char buf[MAXBUF];

ptrdiff_t where;

p=buf;              /* инициализируем указатель */

while(/* некоторое условие */)

 {

    . . .

   p+=somethig;    /* формируем новое значение указателя */

    . . .

   where=pbuf;  /* проверяем значение индекса */

 }

Заголовочный файл <stdlib.h> объявляет множество стандартных библиотечных функций С и типов (таких, как size_t ), в нем также определена константа NULL, которая представляет «нуль» или недействительный указатель. Это нулевое значение, такое, как 0 или ‘((void *) 0)’. Явное использование 0 относится к С++, в С более предпочтительно использовать NULL.

2.1.1. Начальное выделение памяти: malloc()

…Коня, коня, пол царства за коня!. (Уильям Шекспир)

Ура, вот вы и определились с тем, сколько и чего вам надо. Для работы программы, вашего алгоритма потребовалась память. Сначала память выделяется с помощью функции malloc(). Передаваемое значение является общим числом затребованных байтов, то есть размером в байтах непрерывной области памяти, необходимой нам для работы. Возвращаемое значение является указателем на вновь выделенную область памяти или NULL, если память выделить невозможно.

struct coord         /* 3D координаты*/

 {

    int x,y,z;

 } *coordinates;

unsigned int count;  /* сколько нам нужно */

size_t amount;       /* общий размер памяти */

. . .

/* инициализируем переменную amount нужным значением */

/* вычисляем размер требуемой области памяти в байтах*/

amount=count*sizeof(struct coord);

/* выделение памяти */

coordinates=(struct coord *)malloc(amount);

if(cootdinates==NULL) /* в случае ошибки выделения памяти */

 {

    printf(“\nНет доступной динамической памяти!”);

    printf(“\nДля завершения работы программы”

           “ нажмите на любую клавишу.”);

    getch();

    exit(-1);  /* принудительно завершить работу программы */

 }

/* ...использовать координаты... */

Базовый алгоритм выделения динамической памяти:

  1.  Объявить указатель соответствующего типа для выделения памяти.
  2.  Вычислить размер выделяемой памяти в байтах. Для этого нужно умножить число нужных объектов на размер каждого из них. Последний получается с помощью оператора С sizeof. Таким образом, исходный код остается правильным и переносимым.
  3.  Выделить память с помощью malloc(), присвоив возвращаемое функцией значение переменной указателя. Хорошей практикой является приведение возвращаемого malloc() значения к типу переменной, которой это значение присваивается. В С это не требуется, хотя компилятор может выдавать предупреждение. Однако, настоятельно рекомендуется всегда явно приводить тип возвращаемого функцией значения. К тому же, в С++ присвоение значения указателя одного типа указателю другого типа требует операции явного приведения типа, каков бы ни был контекст. Для управления динамической памятью программы на С++ должны использовать new и delete, а не malloc() и free(), чтобы избежать проблем с типами.
  4.  Проверить возвращаемое значение. Никогда не предполагайте, что выделение памяти было успешным. Если выделение памяти завершилось неудачей, malloc() возвращает NULL. Если использовать значение без проверки, программа может быть немедленно завершена из-за нарушения сегментации, которое является попыткой использования памяти за пределами своего адресного пространства.

Следует учесть, блок памяти, возвращенный malloc(), не инициализирован. Диспетчер памяти предоставляет его функции malloc() таким как он есть; программы "работают", занимают выделенную им память, по завершению работы диспетчер выгружает их из памяти не удаляя "следы их работы" из оперативной памяти. Освобожденный блок затем выделяется другой программе и так далее.

Необходимо сразу же инициализировать нужными значениями или хотя бы нулями. Для этого вы можете воспользоваться либо вызовом низкоуровневой функции memset(), либо организовать цикл обращения к данному блоку.

void *memset( void *dest, int c, size_t count );

Например:

/* Пример кода, совместимого с Microsoft Visual C++ */

#include <memory.h>  /* memset() */

#include <stdio.h>

#include <malloc.h>  /* объявление malloc(), в Borland C++ */

                    /* подключается библиотека alloc.h    */

#include <stdlib.h>  /* для использования exit()           */

#include <process.h> /* подключаются две библиотеки        */

int main( void )

 {

    char *buffer;

    if((buffer=(char *)malloc(41*sizeof(char)))==NULL)

      {

         printf("\nНет доступной динамической памяти!\n");

         exit(-1);

      }

    printf("\nПример использования динамической памяти:\n");

    printf( "До заполнения памяти: %s\n", buffer );

    memset( buffer, '*', 40 );

    memset( buffer+40, '\0', 1 );

    printf( "После заполнения:  %s\n", buffer );

    free(buffer);

    return 0;

}

В функция следующем примере функция malloc() используется для выделения памяти под текстовый(символьный) буфер.

Пример:

#include <stdio.h>

#include <string.h>

#include <alloc.h>

#include <process.h>

int main(void)

 {

    char *str;

  /* Выделить память для символьного массива и проверить     */

  /* результат – указатель, возвращенный вызовом malloc()    */

    if ((str = (char *) malloc(10)) == NULL)

      {

  /*   Если возвращенное значений – NULL, выводим            */

  /* сообщение: "Нет доступной памяти для размещения буфера" */

         printf("Not enough memory to allocate buffer\n");

         exit(1);   /* "Аварийно" завершаем работу программы */

  }

                    /* копируем "Hello" в символьный массив  */

    strcpy(str, "Hello");

    printf("String is %s\n", str);     /*  Выводим на экран  */

    free(str);                         /* Освобождаем память */

    return 0; /* Возвращаем управление операционной системе  */

 }

В обоих выше приведенных примерах был использован вызов функции free() для освобождения динамически выделенной памяти. Давайте разберем ее подробнее.

2.1.2. Освобождение памяти: free()

…Господи, ну почему же все ему, за что?

…Да потому, что ты жадный, а даже Бог велел делиться!

(Фрагмент диалога из фильма "Свой среди чужих, чужой среди своих")

Память у компьютера одна для всех программ и ее физический размер ограничен. Программы, запущенные на компьютере, конкурируют за такие машинные ресурсы, как оперативная память и процессорное время. Когда вы завершили использование памяти, "верните ее обратно", использую функцию free().

void free( void *memblock );

Функция free() освобождает блок памяти, ранее выделенный посредством вызова функции malloc(), calloc() или realloc(). Ее единственный аргумент – указатель на блок ранее выделенной памяти. Можно даже передать функции free() пустой указатель.

После вызова free( memblock ) доступ к памяти, на которую указывает memblock запрещен. Этот указатель теперь доступен в других процедурах, если в этом возникнет потребность. Однако существует несколько типичных ошибок, которых нужно избегать:

1. Доступ к освобожденной памяти.

После освобождения памяти переменная указатель продолжает указывать на блок памяти, который больше не принадлежит приложению. Это называется зависшим указателем (dangling pointer). В некоторых системах диспетчер памяти может позволить вам обратиться к уже не принадлежащему вашей программе адресному пространству, по крайней мере, до следующего выделения или освобождения памяти. На других системах такой доступ работать не будет. Во избежание такого рода ошибок целесообразно сразу же устанавливать значение освободившегося указателя в NULL.

int *coordinates;

. . .

free(coordinates);

coordinates = NULL; /* на всякий случай занулим */

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

2. Повторное освобождение одного и того же указателя.

Это создает "неопределенное поведение". После передачи блоков памяти менеджеру памяти они могут быть объединены с другими уже свободными блоками памяти. Этот процесс получил название "сборка мусора". Освобождение чего-то уже освобожденного ведет к неразберихе и может привести к краху системы.

3. Попытка освобождения указателя, полученного не от функций malloc(), calloc() или realloc().

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

float *pt;

pt=(float *)malloc(100*sizeof(float));

. . .

free(pt+30);

Однако, помимо нас информацию о процессе динамического распределения памяти хранит и диспетчер памяти. Когда free() пытается использовать указатель, диспетчер проверяет "наши" действия над ним, по крайней мере, в хорошей системе, он должен это сделать.

4. Выход за пределы буфера.

Языки С и С++ позволяют программисту обращаться с памятью достаточно, не учитывая возможность выхода за пределы выделенной области. Это значит, что можно обратиться как к несуществующему элементу массива, так и к ячейке памяти за пределами динамически выделенного блока. То, что это возможно, еще не значит, что именно так и надо делать. Скорее наоборот, так лучше не поступать! Обращение за пределы адресного пространства программы может привести как уничтожению учетных данных, так и к краху системы. Наиболее типичное последствие выхода за пределы динамически выделенного блока  - срыв стека процесса.

5. Отказ в освобождении памяти.

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

Ниже приводится пример программы, скомпилированной и протестированной в Microsoft Visual Studio .NET 2003 как Win32 Console Application. Она "выжрала" всю оперативную память, 512 MB, менее чем за одну минуту, что привело к "зависанию" компьютера. Настоятельно рекомендую не делать так.

#include <stdio.h>

#include <malloc.h>

#include <stdlib.h>

int main(void)

 {

    int i=0, *a;

    while(1)

      {

  if((a=(int *)malloc(100000*sizeof(int)))==NULL) break;

  printf("\n%d",i++);

      }

    return 0;

 }

В тоже время, данный код, с заменой подключаемой библиотеки malloc.h на alloc.h в среде Borland C++ 3.1 закончил работу, как только была исчерпана вся доступная для DOS-приложения память.

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

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

2.1.3. Изменение размера блока выделенной памяти: realloc()

Динамическая память имеет существенное преимущество перед статической, поскольку позволяет использовать столько памяти, сколько это действительно нужно. К тому же размер динамически выделенной памяти можно изменить. Изменение размера осуществляется с помощью вызова функции realloc().

void *realloc(void *block, size_t size);

Первый аргумент – указатель на исходную, подлежащую изменению, область памяти, второй – новый размер блока в байтах. В случае "удачи" функция возвращает указатель на новый блок памяти, либо NULL, в случае, если нет возможности выделить блок нужного размера, либо если новый размер блока равен нулю. При увеличении размера памяти realloc() выполняет побайтное копирование содержимого исходной области памяти, подлежащей изменению, в новую. При сокращении размера блока данных realloc() часто обнуляет внутреннюю учетную информацию и возвращает тот же указатель. Это избавляет от необходимости копировать первоначальные данные. Однако если такое случится, не думайте, что можно использовать память за пределами нового размера. Она, память, уже принадлежит не вам, а диспетчеру памяти и тот использует ее по своему усмотрению для нужд вашей же программы. Но как он это будет делать, вам заранее не известно!

#include <stdio.h>

#include <alloc.h>

#include <string.h>

int main(void)

 {

    char *str;

    /*  Выделение памяти для символьного массива  */

    str = (char *) malloc(10);

    /* В данном примере без проверки на ошибку... */

    /* Копируем "Hello" в строку */

    strcpy(str, "Hello");

    printf("Строка: %s\n  Адрес: %p\n", str, str);

    str = (char *) realloc(str, 20);

    printf("Строка: %s\n  Новый адрес: %p\n", str, str);

    /* Освобождение памяти */

    free(str);

    return 0;

 }

Алгоритм использования функции realloc():

  1.  Вычислить новый размер выделяемой области в байтах.
  2.  Вызвать realloc() с оригинальным указателем, полученным от malloc(), либо calloc() с новым размером.
  3.  Проверить значение результата вызова на NULL – отказ в перераспределении памяти/ошибка.
  4.  В случае удачного вызова привести тип и присвоить возвращенное realloc() значение указателю.

При работе с функцией realloc() целесообразно использовать временную переменную указатель того же типа, что и используемый вами. Дело в том, что когда realloc() возвращает NULL, первоначальный указатель все еще действителен; можно безопасно продолжить использовать эту память. Но если присваиваете указателю значение NULL, возвращенное "неудачным" вызовом realloc(), вы теряете указатель на первоначальную память. Эту память больше нельзя использовать и ее уже невозможно освободить! Это создает утечку памяти.

int *ptr, *temp;

ptr=(int *)malloc(10*sizeof(int));

. . .

temp=(int *)realloc(ptr,20*sizeof(int));

if(temp==NULL)

 {

    /* Проблема - невозможно перераспределить память */

   . . .

 }

else

 {

    free(ptr); /* Теперь исходный блок памяти уже не нужен */

    ptr=NULL;  /* Во избежания "утечки" освободите память  */

    ptr=temp;  /* В дальнейшем пользуйтесь новым блоком    */

   . . .

 }

В следующем примере происходит динамическое выделение памяти по строку, вводимую пользователем, именно того размера, какого необходимо. Однако учтите, что за выигрыш при выделении памяти, приходится "платить" дополнительными вызовами функции перераспределения памяти realloc().

#include <stdio.h>

#include <conio.h>

#include <alloc.h>

#include <string.h>

#include <stdlib.h>

int main(void)

 {

    char *str = NULL;

    char ch; /* Используется для хранения кода нажатой клавиши */

 

    int curLen=2, curPoint=0;   /* curLen-текущая длина строки */

       /* curPoinе-текущая позиция записи кода символа в строку*/

    str = (char *) calloc(2, sizeof(char));

    if(str==NULL)

      {

     printf("\nНет доступной динамической памяти!");

     exit(1);

      }

/* Строка вводится до тех пор, пока пользователь не нажмет Enter*/

    while((ch=getch())!=13)

      {

     putch(ch); /* Эхо-отображение считанного символа */

     *(str+curPoint)=ch;

     curLen++;

     curPoint++;

     str=(char *)realloc(str,curLen);

    *(str+curPoint)='\0'; /* Признак конца строки */

      }

    printf("\nВведенная строка: %s\n", str);

    free(str);

    getch();

    return 0;

 }

Как ранее отмечалось, использование одного и того же указателя в операции перераспределения памяти str=(char *)realloc(str,curLen); может привести к "потере" ранее выделенной области памяти, в случае если нет доступного блока нужного размера.

2.1.4. Выделение памяти с инициализацией нулями: calloc()

Функция calloc() является простой оболочкой вокруг malloc(). Главным ее преимуществом является то, что она обнуляет динамически выделенную память.

void *calloc(size_t numb_items, size_t size);

Где numb_items – количество элементов данных, а size – их размер.

Вот одна из возможных реализаций функции calloc():

void *calloc(size_t numb_item, size_t size)

 {

    void *p;

    size_t total;

    total=numb_item*size;    /* Вычислить размер */

    p=malloc(total);         /* Выделить память */

    if(p!=NULL)              /* Если нет ошибок */

      memset(p, '\0',total); /* Заполнить ее нулями */

    return p;

 }

2.2. Выделение памяти из стека: alloca()

Функция alloca() ( _alloca() для Microsoft реализации языка C) выделяет память из стека.

Вот основные причины, по которым следует избегать использования этой функции:

  1.  функция не является стандартной; она не включена ни в какой стандарт, ни в ISO, ни в С или POSIX.
  2.  функция не переносима: реализована не на всех платформах.
  3.  очень сильно зависима от машины и компилятора. На многих системах ее реализация ошибочна!
  4.  Нарушает структуру внутреннего пространства стека при вызове внутри списка аргументов функции.
  5.  Потворствует неряшливому программированию.

2.3. Выделение памяти для многомерных структур

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

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

#include <stdio.h>

#include <conio.h>

#include <alloc.h>

#include <string.h>

#include <stdlib.h>

int main(void)

 {

    int **Arr = NULL;

    int numColums,numRows;

    int i;

    do

      {

    printf("\nВведите количество строк: ");

    scanf("%d",&numRows);

      }while(numRows<=0);

    Arr = (int **)calloc(numRows, sizeof(int *));

    if(Arr==NULL)

      {

  printf("\nОшибка при выделении памяти "

             "под массив указателей!");

  exit(1);

      }

    for(i=0;i<numRows;i++)

      {

     printf("\nВведите количество столбцов в %d строке: ",i);

     scanf("%d",&numColums);

     *(Arr+i)=(int *)calloc(numColums,sizeof(int));

           . . .

      }

      . . .

    for(i=0;i<numRows;i++)

      free(*(Arr+i));

    free(Arr);

    getch();

    return 0;

 }

Данный механизм наиболее часто используется для обработки строк, в том числе и для формирования хэш-таблиц.

3. Выделение памяти в C++

3.1. Управление памятью с помощью new и delete

Язык C++, как и ANSI С умеет работать с тремя видами памяти: статической, автоматической (стек) и динамической.

Глобальные переменные располагаются в статической области памяти. Локальные переменные располагаются в стеке времени выполнения при вызове функции или при входе в блок. После завершения функции память автоматически освобождается.

При работе в С++ вам доступны все средства управления динамической памятью, присущие языку ANSI C, такие как malloc(), calloc() и realloc(). В то же время в ваше распоряжение поступают два совершенно новых оператора new и delete, которые могут делать то же самое, что и стандартные функции и даже более.

Оператор new используется для выделения памяти динамической переменной, а оператор delete – для освобождения памяти.

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

#include <iostream>  /* Поддержка надмножества языка С++ */

#include <cstring>   /* версии Standard C++ */

#include <new.h>

#include <stdlib.h>

#include <windows.h>

using namespace std;

struct q { int ia[1024]; };

char msg1[81];

int main(void)

 {

   srtuct q *qp;        /* Указатель на структуру q */

   strcpy(msg1,"\n Использование динамической памяти в С++\n");

   CharToOem(msg1,msg1);

   cout<<msg1<<endl;

   set_new_handler(0);  /* Указание оператору new вернуть 0 */

                        /* в случае неудачи */

   for(;;)  /* Порождаем "бесконечный" цикл обращения к памяти */

     {

        qp=new q;       /* Выделить память под новый экземпляр */

                        /* структуры q */

        cout<<qp<<endl; /* Вывести адрес структуры */

        if(qp==0)       /* Если new вернет 0 */

          {

             strcpy(msg1,"\nНет доступной динамической памяти!");

             CharToOem(msg1,msg1);

             cout<<msg1<<endl;

          }

     }

   return 0;

 }

3.2. Обнаружение ошибок, связанных с нехваткой памяти

При создании динамических переменных необходимо проверять, достаточно ли свободной памяти. Если операция new завершилась успешно, результатом ее вызова является указатель на выделенную память требуемого размера. В случае неудачного завершения (нет свободной памяти требуемого размера) возможны две ситуации:

  1.  new возбуждает исключительную ситуацию;
  2.  new возвращает ноль.

Функция set_new_handler(0) устанавливает адрес обработчика (подпрограммы времени выполнения), используемой new. Обработчик по умолчанию заставляет new возбуждать исключительную ситуацию для ошибок.

Обратите внимание на следующий пример.

#include <iostream>

#include <new>

#include <stdlib>

struct q { int ia[1024];};

using namespace std;

int main(void)

 {

    struct q *qp;      /* Объявляем на указатель */

    set_new_handler(0); /* Указание new вернуть 0 в случае неудачи */

    for(;;)  /* "Бесконечный цикл" с утечкой памяти ... */

      {

         qp=new q; /* Выделить память структуру типа q */

         cout<<qp<<endl;

         if(qp==0) /*   В случае возврата new 0 следует   */

           {       /* следует вывести сообщение об ошибке */

              cout<<"Нет доступной памяти!"<<endl;

              exit(1); /* Принудительно прервать программу */

           }

      }

    return 0;

 }

Данная программа, к несчастью, не бесспорна. Лишь на первый взгляд она может показаться лишенной как-либо недочетов. В чем же дело, спросите вы? А дело в том, что она очень сильно зависима от особенностей программной платформы, на которой будет скомпилирована и запущена. Да, она практически на 100% безупречна как DOS-приложение. Но как Windows-приложение она опять же приводит к утечке памяти. В Windows диспетчер памяти дает нам ее "слишком много", и функция set_new_handler() срабатывает слишком поздно. Пока вы не наберетесь достаточного опыта программирования, просто примите это к сведению. Соотносите "свой аппетит" и возможности компьютера.

Вернемся к функции set_new_handler(), она вызывается с аргументом, равным нулю, чтобы оператор new возвращал нуль в случае ошибки.

#include<new>

#include<iostream>

using namespace std;

void __cdecl newhandler( )

 {

    cout << "The new_handler is called:" << endl;

    throw bad_alloc( );

    return;

 }

int main( )

 {

    set_new_handler (newhandler);

    try // try-catch блок обработки исключений

    {

       while ( 1 )

       {

          new int[5000000];

          cout << "Allocating 5000000 ints." << endl;

       }

    }

    catch ( exception e )

    {

       cout << e.what( ) << " xxx" << endl;

    }

    return 0;

 }

3.3. Использование простых динамических переменных

3.4. Использование динамических строк

3.5. Использование динамических массивов

  1.  Динамическое распределение памяти

Библиотека языка Си предоставляет механизм распределения динамической памяти (heap). Этот механизм позволяет динамически (по мере возникновения необходимости) запрашивать из программы дополнительные области оперативной памяти.

Работа функций динамического распределения памяти различается для различных моделей памяти, поддерживаемых системой программирования (смотри первую части книги).

В малых моделях памяти (tiny, small, medium) доступно для использования все пространство между концом сегмента статических данных программы и вершиной программного стека, за исключением 256-байтной буферной зоны непосредственно около вершины стека.

В больших моделях памяти (compact, large, huge) все пространство между стеком программы и верхней границей физической памяти доступно для динамического размещения памяти.

Следующие функции используются для динамического распределения памяти:

Функция

Краткое описание

calloc

выделить память для массива

free

освободить блок, полученный посредством функции calloc, malloc или realloc

malloc

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

realloc

переразместить ранее выделенный блок памяти, изменив его размер

sbrk

переустановить адрес первого байта оперативной памяти, недоступного программе (начала области памяти вне досягаемости программы)

Система программирования MSC предоставляет дополнительно функции:

Функция

Краткое описание

alloca

выделение блока памяти из программного стека

_expand

изменение размера блока памяти, не меняя местоположения блока

_ffree

освобождение блока, выделенного посредством функции fmalloc

_fmalloc

выделение блока памяти вне данного сегмента

_freect

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

_fmsize

возвращает размер блока памяти, на который указывает дальний (far) указатель

halloc

выделить память для большого массива (объемом более 64 Кбайтов)

hfree

освободить блок памяти, выделенный посредством функции halloc

_memavl

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

_msize

определить размер блока, выделенного посредством функций calloc, malloc, realloc

_nfree

освобождает блок, выделенный посредством _nmalloc

_nmalloc

выделить блок памяти в заданном сегменте

_nmsize

определить размер блока, на которой указывает близкий (near) указатель

stackavail

определить объем памяти, доступной для выделения посредством функции alloca

Система программирования ТС предоставляет дополнительно функции:

Функция

Краткое описание

brk

переустановить адрес первого байта оперативной памяти, недоступного программе (начала области памяти вне досягаемости программы)

allocmem

низкоуровневая функция выделения памяти

freemem

низкоуровневая функция возврата памяти операционной системе

coreleft

узнать, сколько осталось памяти для выделения в данном сегменте

farcalloc

выделить блок памяти вне данного сегмента

farcoreleft

определить, сколько памяти для размещения осталось вне данного сегмента

farmalloc

выделить блок памяти вне данного сегмента

farrealloc

изменить размер блока, ранее выделенного функцией farmalloc или farcalloc

farfree

освободить блок, ранее выделенный функцией farmalloc или farcalloc

Прототипы функций содержатся в файле malloc.h для системы программирования MSC и в файле alloc.h для системы программирования ТС.

Функции calloc и malloc выделяют блоки памяти, функция malloc выделяет заданное число байтов, тогда как calloc выделяет и инициализирует нулями массив элементов заданного размера.

Функции _fmalloc и _nmalloc подобны malloc, за исключением того, что _fmalloc и _nmalloc позволяют выделить блок байтов в том случае, когда существуют ограничения адресного пространства текущей модели памяти. Функция halloc выполняется аналогично calloc, но halloc выделяет память для большого массива (больше 64 К).

Функции realloc и _expand изменяют размер полученного блока.

Функция free (для calloc, malloc и realloc), функция ffree (для _fmalloc), функция _nfree (для _nmalloc) и функция hfree (для halloc) освобождают память, которая была выделена ранее, и делают ее доступной для последующего распределения.

Функции _freect и _memavl определяют: сколько памяти доступно для динамического выделения в заданном сегменте; _freect возвращает примерное число областей заданного размера, которые могут быть выделены; _memavl возвращает общее число байтов, доступных для выделения.

Функции _msize (для calloc, malloc, realloc и _expand), _fmsize (для _fmalloc) и _nmsize (для _nmalloc) возвращают размер ранее выделенного блока памяти.

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

Все выше описанные функции распределяли области памяти из общей памяти. Система программирования MSC предоставляет 2 функции, alloca и stackavail, для выделения памяти из стека и определения количества доступной памяти в стеке.


 

А также другие работы, которые могут Вас заинтересовать

66215. РЕФОРМАТОРСЬКА ПЕДАГОГІКА ЗАРУБІЖНИХ КРАЇН НАПРИКІНЦІ 19 – НА ПОЧАТКУ 20 СТОЛІТТЯ 86.5 KB
  Перед масовою народною школою постало завдання розробити нові форми навчання і виховання з метою підняття рівня освіти. При Феррі 1832-1893 було запроваджено автономію університетів розроблено нові навчальні плани середньої школи в яких більше уваги приділялось вивченню природничих наук...
66216. Деловая оценка персонала в организации 60.5 KB
  Структура процесса требования к проведению аттестации Понятие виды функции оценки персонала Деловая оценка – процесс определения эффективности деятельности сотрудников в ходе реализации задач организации. Объектом аттестации может быть отдельный сотрудник рабочее место соответствие его требованиям...
66218. ОСВІТА І ПЕДАГОГІЧНА ДУМКА В УКРАЇНІ ВІД КИЇВСЬКОЇ РУСІ ДО ПОЧАТКУ 20 СТОЛІТТЯ 235.5 KB
  Давні слов'янські племена, за даними археології і за свідченнями візантійських джерел, відзначались такими рисами: загострене почуття спільності і справедливості, стійка віра у верховного бога, віра в магію, добропорядність, військова навченість, мужність, фізичний розвиток, загартованість, витривалість.
66219. Движение и развитие персонала организации 73.5 KB
  Структура и методы обучения Основные направления профессионального развития персонала Развитие персонала совокупность мероприятий в области обучения повышения профессионального мастерства и квалификации персонала.
66220. Методы программирования. Объектно-ориентированное программирование 29.25 KB
  Структурное программирование Основные положения концепции структурного программирования были сформулированы в 60х годах XX века голландским ученым Э. Технология структурного программирования зиждется на идее о выделении множества базисных элементов...
66221. Освіта в Україні з 1917 р. до початку 21 століття 85 KB
  Навчання у школах і вищих навчальних закладах переводилось на українську мову. Значна частина шкіл перейшла на навчання українською мовою. Кошти на освітні реформи в основному давала громадськість: пожертви з боку багатих інтелігенції земств сільські сходи часто постановляли зібрати кошти на організацію навчання.
66222. Управление деловой карьерой персонала организации 97 KB
  Технология формирования резерва руководителей Кадровый резерв это группа руководителей и специалистов обладающих способностью к управленческой деятельности отвечающих требованиям предъявляемым должностью того или иного ранга подвергшихся отбору...