Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
AllCandC++Lectures.docx
Скачиваний:
2
Добавлен:
25.09.2019
Размер:
52.56 Кб
Скачать

Динамическое распределение памяти Статическое выделение памяти

Когда компилятор в начале блока встречает определение переменной или массива, то он выполняет следующие действия: 1) вычисляет размер, необходимый для хранения данных 2) выделяет необходимый объем памяти 3) запоминает, для какого типа данных он выделил память и дальше пользуется этим при операциях с данными.

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

Динамическое выделение памяти

Предположим, в программе используется массив, длина которого станет известной только в ходе выполнения программы. Механизм статического распределения позволяет решить эту задачу, но не рациональным способом. А именно: задать для массива размер, который заведомо подходит для всех случаев. Так как компилятор не может оптимально решить эту задачу, то ее решение перекладывается на программиста. Для этого используется механизм динамического выделения памяти. Программист должен делать то же что и компилятор: 1) вычислить размер создаваемого блока данных 2)предусмотреть выделение памяти и запоминание адреса этой памяти 3) учитывать в операциях тип данных 4) если объект данных становится не нужен, то программист должен предусмотреть освобождение памяти!

С динамическим выделением памяти связаны два типа ошибок: 1) "повисшие" указатели. Предположим, была выделена память, и ее адрес мы запомнили в некоторой переменной. Затем память освободили, а переменная продолжает хранить адрес, который теперь указывает на неизвестные данные. Т.е. блока данных нет, а указатель есть. 2) "зомби" блоки. Механизм динамического выделения памяти никак не связан с понятиями областей видимости. Это означает, что блок памяти остается зарезервированным до тех пор, пока мы его явно не освободим. В отличие от этого, переменная, которая хранит адрес блока, имеет свою область действия. При выходе из нее переменная уничтожается. Это означает, что блок памяти выделен, а обратиться к нему мы не можем. В том числе и для того чтобы его освободить. Такие блоки засоряют память, и, в конце концов, может оказаться, что программе не хватает памяти для выполнения очередной операции выделения памяти. Это часто происходит, если память выделяется в цикле. За освобождение ответственен программист.

Библиотека c для управления динамическим выделением памяти

Функции для динамического выделения памяти выделяют требуемые блоки памяти в куче. Библиотечные функции описаны в двух заголовочных файлах: alla.h, stdlib.h

Функции динамического выделения памяти никак не связаны с типом данных. Они выделяют заданное кол-во байт. Адрес выделенного блока никак не связан ни с каким из типов. Поэтому он имеет тип void*(в английском языке понимается как generic). Указатели типа void* могут хранить адреса любых типов данных и, что более важно, могут быть преобразованы к указателю на любой тип.

1. Выделение памяти при помощи функции malloc:

Void* malloc( size_t n_byte );

Size_t это синоним unsigned int. Специально введем, чтобы употреблять в тех местах, где речь идет о размерах данных.

N_byte это кол-во байт, которое необходимо выделить программе.

Детали работы механизма выделения памяти зависят от компилятора, но, в общем, они используют следующие принципы: память выделяется не байтами, а блоками байт, например: 8, 16. Это означает, что если программа запросила 10 байт, то для нее будет выделено два блока по 8, или 1 по 16. Если затребованный объем памяти предполагает выделение нескольких блоков по 8 или 16, то эти блоки связываются в список, а информация, необходимая для поддержания списка, хранится в служебных байтах, добавляемых к каждому блоку.

2) выделения памяти с помощью функции calloc:

Void* calloc( size_t n_blocks, size_t n_bytes );

Эта функция выделяет память для n-blocks блоков памяти, размерами n_bytes. В отличие от malloc, эта памяти сразу обнуляется.

Пример 1. Использование указателя на void:

Unsigned int ui=0xabcd;

Void *pv=&ui;(просто запоминает адрес)

Unsigned char *puc=(unsigned char*)pv;(мы преобразовали к типу)

Printf("%x\n %x\n", puc[0], puc[1]);

Printf(" %x\n", ++(unsigned char*)pv); (будет ab)

Пример 2. Динамическое выделение памяти:

Пусть необходимо выделить память для пяти значений типа int и присвоить им числа 1,2,3,4,5.

Подключаем заголовочные файлы #include <alloc.h>

Void *pv;

Pv=malloc(5*sizeof(int));

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

If(pv!=NULL) {

For(i=0; i<5; i++)

(Int*)pv[i]=i+1;

}

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]