Cpp intro lec 4

Page 1

Лекция 4. Указатели и динамическая память Указатели и динамическая память Динамическая память - это оперативная память ЭВМ, предоставляемая программе при её работе. Динамическое размещение данных означает использование динамической памяти непосредственно при работе программы. В отличие от этого статическое размещение данных осуществляется компилятором, то есть должны быть известны заранее количество и тип размещаемых данных. При динамическом размещении они могут быть заранее не известны. Оперативная память ЭВМ представляет собой совокупность ячеек для хранения информации – байтов, каждый из которых имеет собственный номер. Эти номера называются адресами, они позволяют обращаться к любому байту памяти. Язык С++ предоставляет в распоряжение программиста средство управления динамической памятью – так называемые указатели (pointer). Указатель – это переменная, которая в качестве своего значения содержит адрес байта памяти. С помощью указателей можно размещать в динамической памяти любой тип данных. Лишь некоторые из них занимают во внутреннем представлении один байт, остальные – несколько смежных. Поэтому на самом деле указатель содержит адрес только первого байта данных. На рисунке показана иллюстрация понятий «адрес» и «указатель».

адреса 0000

ячейки (байты) памяти

int i;

0001

первый байт переменной i

0002 0003 0004

указатель на переменную i

0005 В С++ указатели всегда связываются с некоторым типом данных (при этом говорят, что указатель ссылается на соответствующий тип). Хотя адрес – это по существу целое число,

© В.М. Гриняк, доц. каф. ИСПИ ВГУЭС


определяющее положение объекта в оперативной памяти ЭВМ, указатель как бы «помнит» на какого рода данные он ссылается. Объявление указателя выглядит так: тип_объекта *имя_указателя; Пример: int *aa; double *bb; Чтобы получить доступ к объекту (переменной), на который указатель ссылается, применяют операцию разыменования указателя *. Например, *aa будет представлять собой значение переменной, на которую ссылается указатель aa. Чтобы, наоборот, получить адрес переменной, нужно применить операцию взятия адреса &. Пример: double pi=3.14; double *aa; aa=π // Здесь указателю аа присваивается адрес переменной pi. printf(“%f”, *aa); // Здесь на печать выводится значение, содержащееся по адресу аа. Выделение и освобождение динамической памяти, размещение в ней переменных Основное назначение указателей - создание и обработка динамических структур данных. В С++ можно выделить память под некоторый объект не только с помощью оператора объявления, но и динамически, во время исполнения программы. Выделение памяти для размещения объекта производится с помощью функции malloc( ) (для доступа к ней необходимо сослаться на заголовки <alloc.h> и <stdlib.h>). Пример: //-----------------------------------------------------------double *aa; //Объявление указателя на тип double aa=(double*)malloc(sizeof(double)); // Динамическое выделение памяти размера // sizeof(double) *aa=2.71;

// присвоение значения 2.71 динамической переменной, размещённой по // адресу aa © В.М. Гриняк, доц. каф. ИСПИ ВГУЭС


printf(“%f”,*aa); // вывод на печать значения динамической переменной free(aa); // Освобождение памяти, закреплённой за указателем аа. //------------------------------------------------------------Несколько пояснений к примеру. Аргументом функции malloc( ) является размер области памяти, которую нужно выделить; для этого можно применить операцию sizeof( ), которая возвращает размер переменной или типа, указанного в качестве операнда. Функция malloc( ) возвращает значение типа void* – то есть указатель на пустые данные. Такой указатель нельзя разыменовывать, поскольку неизвестно, на данные какого типа он указывает, поэтому к нему применяется операция приведения типа (double*). Память, выделенную malloc( ), следует освободить функцией free( ), если динамическую переменную больше не предполагается использовать. Указатели и массивы Между указателями и массивами в С++ существует тесная связь. Имя массива без индекса эквивалентно указателю на его первый элемент. И наоборот, указатель можно использовать подобно имени массива, то есть индексировать его. Например: //--------------------------------int arr[5], i; int *aa; aa=arr; // указателю аа присваивается адрес первого элемента массива arr. // это эквивалентно записи aa=&arr[0]; i=aa[2]; // обращение к элементу массива arr с индексом [2]. //--------------------------------Приведём пример динамического объявления одномерного массива длины n: //------------------------------------int *aa; int n; n=10; aa=(int*)malloc(n*sizeof(int)); © В.М. Гриняк, доц. каф. ИСПИ ВГУЭС


// Теперь к массиву aa можно обращаться обычным способом аа[0], aa[1] и т.п. //После завершения с ним работы следует освободить занимаемую им память free(aa); //-----------------------------------Для организации многомерных динамических массивов применяется приём типа «указатель на указатель». На рисунке приведена иллюстрация этого приема

aa

Пример динамического объявления двумерного массива размерностью m на n: //-------------------------------------int **aa; int m, n, i; m=3; n=5; aa=(int**)malloc(m*sizeof(int*)); //Выделение памяти под массив из m указателей на int for(i=0;i<m;i++) { aa[i]=(int*)malloc(n*sizeof(int));// Выделение памяти под массивы из n целых чисел } // Теперь к массиву aa можно обращаться обычным способом аа[0][0], aa[1][0] и т.п. //После завершения с ним работы следует освободить занимаемую им память for(i=0;i<m;i++) © В.М. Гриняк, доц. каф. ИСПИ ВГУЭС


{ free(aa[i]); } free(aa); //-----------------------------------При работе с динамической памятью кроме функции malloc( ) в С++ часто используется также функция calloc( ). Кроме того, существуют также специальные операции new и delete. Они введены для предоставления возможности перегрузки этих операций для придания им каких-либо дополнительных свойств. Вот пример создания и уничтожения динамической переменной с помощью операций new и delete. //--------------------------------int *aa, *bb; int n; n=5; aa=new int; // Выделение памяти под переменную типа int bb=new int[n]; // Выделение памяти под массив значений типа int длины n delete aa;// Освобождение памяти под переменной delete[] bb//; Освобождение памяти под массивом //--------------------------------Использование процедур выделения и освобождения памяти, как и вообще вся работа с динамической памятью требует особой осторожности и тщательного соблюдения следующего правила: освобождать нужно ровно столько памяти, сколько её было зарезервировано и именно с того адреса, с которого она была зарезервирована. Средства windows для работы с памятью Система Windows имеет собственные средства для работы с памятью. Это так называемые API-функции (application programming interface), которые становятся доступными после ссылки на заголовочный файл <windows.h>. Так как почти все устройства ЭВМ (в том числе оперативная память) в многозадачных ОС являются разделяемыми ресурсами, то © В.М. Гриняк, доц. каф. ИСПИ ВГУЭС


есть программное адресное пространство не совпадает с физическим, API-функции в своей работе широко используют понятие системного дескриптора (стандартный тип HANDLE). Дескриптор – это целое число, хранящее условный адрес структуры с описанием той или иной программной сущности (переменной, объекта, области памяти, файла, потока, окна и т.п.). Перечислим основные функции WinAPI, используемые для работы с динамической памятью (полное их описание можно получить, обратившись к справочным файлам WIN32.HLP или WIN32S.HLP). GlobalAlloc( ) – выделяет память требуемого размера; GlobalFree( ) – освобождает блок памяти; GlobalLock( ) – возвращает указатель на первый байт указанного блока памяти (как бы «преобразуя» дескриптор HANDLE в указатель); GlobalHandle( ) – возвращает дескриптор блока памяти, связанного с заданным указателем (как бы «преобразуя» указатель в дескриптор HANDLE); (См. также пример программы к лекции 4). Передача параметров функций по ссылке и по указателю В языке С++ в качестве параметров функций допускается передавать не только значения переменных, но и указатели на переменные. Например: double sum(double *aa, int n) {int i; double j=0; for(i=0;i<n; i++) { j=j+aa[i]; } return j; } Данная функция sum выдаст сумму элементов массива aa (в данном случае память под массив и его инициализация происходит вне тела функции sum; говорят, что массив aa передается в функцию по указателю). Следует помнить, что в случае передачи параметров по указателю, изменение их значений внутри тела функции привёдёт к изменению их значений и вне тела функции. © В.М. Гриняк, доц. каф. ИСПИ ВГУЭС


В С++ имеется также модифицированная форма указателей, называемая ссылкой. Ссылка – это указатель, который автоматически разыменовывается при обращении к нему. Ссылки оказываются удобны для передачи в функцию параметров по указателю, причём позволяют сделать это без использования явных указателей и адресов. Пример передачи параметров функции по ссылке: //-------------------------------------------------------------int i, j; i=1; j=2; void example(int &i, int j)

// переменная i передаётся в функцию как ссылка, // j – как значение

{int k; k=i+j; i=k; j=k; } // Вызов функции example example(i, j); //------------------------------------------------------------Результатом работы данной программы будет i=3, j=2. То есть переменная i, переданная по ссылке (int &i в списке параметров функции example), изменила своё значение, а переменная j, переданная по значению, нет. По ссылке возможна не только передача параметров в функцию, но и передача из неё возвращаемого значения. В том случае, когда функция возвращает ссылку, она фактически возвращает адрес; это делает возможным написание функции слева от оператора присваивания в выражениях, соответствующее значение будет помещено по адресу, возвращённому функцией. int& func(int n) { … }; © В.М. Гриняк, доц. каф. ИСПИ ВГУЭС


func(5) = 10; Помимо случаев, когда функция должна возвращать значения в параметрах, ссылки и указатели могут быть полезны при передаче переменных большого размера, поскольку функции в этом случае передаётся не сама переменная, а её адрес. Смотри также пример к лекции 4, расположенный на портале

© В.М. Гриняк, доц. каф. ИСПИ ВГУЭС


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.