Операция delete

delete p

delete [] p

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

Операция освобождает динамическую память (уничтожает динамические переменные). Если память выделялась под массив, то она должна освобождаться операцией delete []. При отсутствии [] будет освобождена память только от первого элемента массива, остальная выделенная под массив память останется помеченной как занятая. Кроме того адрес неосвобожденной памяти будет недоступен программе (поисходит «утечка памяти»).

Примеры:

int *p1;

p1=new int (5); //выделение и инициализация памяти под целое

//Здесь могут быть операторы, использующие р1

delete p1; //память возвращается в кучу для использования

p1=new int[3]; //выделение памяти под 3 целых числа

//здесь могут быть операторы, использующие р1

delete [] p1; //память возвращается в кучу для использования

6.3. Связь между указателями и массивами

Указатели и массивы в языке С++ взаимосвязаны и могут использоваться в программе почти одинаково. В языке С++ имя массива является константным указателем, содержащим адрес начала массива (адрес 0-элемента). Изменить значение константного указателя нельзя, поэтому нельзя присвоить один массив другому. Но так как массив является указателем, то к нему можно применять некоторые операции, определенные для указателей. Например, можно обращаться к элементам массива с помощью смещения указателя относительно начала массива:

int a[5]={10, 20, 30, 40, 50};

*a=1; //равносильно a[0]=1;

*(a+1)=2; //равносильно a[1]=2;

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

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

char s[10]=”abcdef”; //массив символов

char *ps; //указатель на символ

ps=s; //указатель содержит адрес массива символов s

cout<<s<<endl; //abcdef

cout<<ps;<<endl; //abcdef

ps=&s[2]; // указатель содержит адрес элемента массива символов

cout<<ps;<<endl; //cdef(вывод части строки)

ps[0]=’#’; //изменение содержимого строки через указатель

cout<<ps;<<endl; //#def(вывод части строки)

cout<<s;<<endl; //ab#def(вывод измененной строки)

6.4. Функция strtok для выделения лексем из текста

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

Функция strtok из стандартной библиотеки string используется для выделения лексем из строки. У функции есть два аргумента. При первом вызове первый аргумент – строка, из которой надо выделить первую лексему, а второй – строка разделителей лексем. Результат функции – адрес первой лексемы. Например, для выделения первого слова в тексте, в котором слова разделены пробелами и запятыми, можно использовать код:

char s[81]=”abc fgh, aa”;

char *p;

p=strtok(s,” ,“);

cout<<p<<endl; // abc

Функция находит первый символ ('a'), отличный от разделителя – адрес этого символа и есть адрес первой лексемы. Кроме того, функция находит первый разделитель в строке (' ') и заменяет его 0-символом. Теперь можно вывести строку, заданную указателем р или выполнить с ней другие действия. Функция также находит в строке и сохраняет в своих внутренних переменных адрес следующей лексемы (или 0, если в строке больше нет лексем). Для выделения из строки следующей лексемы функция вызывается повторно, но в качестве первого аргумента функции указывается значение 0. Например,

p=strtok(0,” ,“);

cout<<p<<endl; //fgh

Функция strtok, выделяя из строки лексемы, изменяет строку. Поэтому, если со строкой надо работать далее, ее надо предварительно сохранить в другой переменной.

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

#include <conio.h>

#include <iostream.h>

#include <string.h>

void main()

{

char r[]=" ,.!?:;-"; //массив разделителей

char s[81]; //текст

char word[21]; //слово-образец

char *pw; //адрес очередного слова (лексемы)

int k; //счетчик вхождений слова word в текст

cout<<”s? “;

cin.getline(s,81); //чтение строки с пробелами

cout<<”word? “;

cin>>word;

k=0;

pw=strtok(s,r); //выделение первого слова

while(pw!=0) //цикл выделения и сравнения слов-лексем

{

if (strcmp(pw,word)==0)

k++;

pw=strtok(0,r); //выделение следующего слова

}

cout<<”k=”<<k;

getch();

}

 

6.5. Динамические массивы

 

В языке С++ используются обычные (статические) и динамические массивы. При объявлении статического массива указывается его размер, который не может быть изменен при работе программы. Например, для статического массива int a[100] выделяется 100 ячеек для хранения 100 целых чисел и увеличить или уменьшить выделенную под этот массив память нельзя.

Момент выделения памяти статическому массиву и время существования массива определяется местом объявления массива. Так если статический массив объявлен в функции main, то память под него выделяется перед началом выполнения функции, а сам массив существует все время работы функции main.

Динамическим массивом называется массив, память под который выделяется во время работы программы операцией new. Размер необходимой памяти под динамический массив можно задавать при выполнении программы. Можно управлять моментом выделения памяти под массив, располагая в нужном месте операцию new. Можно освободить память из-под динамического массива, когда потребность в массиве исчезнет, используя в нужном месте кода программы операцию delete. Размер динамического массива можно программно изменить.

Пример программы, использующей динамические массивы для последовательной обработки двух текстовых файлов. В файле a.txt записаны целые числа. Первое число файла – количество чисел в файле. В файле b.txt записаны вещественные числа. Первое число файла – целое число, задающее количество чисел в файле. Найти три наибольших числа в каждом файле. Файлы с числами находятся в одной с программой папке.

#include <conio.h>

#include <iostream.h>

#include <fstream.h> //библиотека для работы с файлами

void main()

{

ifstream fa; //файловая переменная, связанная с файлом a.txt

ifstream fb; //файловая переменная, связанная с файлом b.txt

int *a; //указатель на массив с числами файла a.txt

float *b; / указатель на массив с числами файла b.txt

int n; //количество чисел в файле

int i, j; //номер элемента в массиве и номер прохода массива

int xa; //переменная для перестановки при сортировке

float xb; //переменная для перестановки при сортировке

fa.open("a.txt"); //открытие файла для чтения

fa>>n; //чтение первого числа из файла

a=new int [n]; //выделение памяти для динамического массива

for (i=0; i<n ;i++) //чтение файла в динамический массив

fa>>a[i];

fa.close(); //закрытие файла

for (j=1; j<=3; j++) //поиск 3 самых больших чисел

for (i=0; i<n-j; i++)

if (a[i]>a[i+1])

{ xa=a[i]; a[i]=a[i+1]; a[i+1]=xa;}

for (i=n-1; i>=n-3; i--) //вывод 3 самых больших чисел

cout<<a[i]<<" ";

cout<<endl;

delete []a; //освобождение памяти

//Далее обработка другого файла

fb.open("b.txt"); //открытие файла для чтения

ba>>n; //чтение первого числа из файла

b=new float [n]; //выделение памяти для динамического массива

for (i=0; i<n; i++) //чтение файла в динамический массив

fb>>a[i];

fb.close(); //закрытие файла

for (j=1; j<=3; j++) //поиск 3 самых больших чисел

for (i=0; i<n-j; i++)

if (b[i]>b[i+1])

{ xb=b[i]; b[i]=b[i+1]; b[i+1]=xb;}

for (i=n-1; i>=n-3; i--) //вывод 3 самых больших чисел

cout<<b[i]<<" ";

getch();

}

 

7. Структуры и объединения

 

Для работы со сложными данными в языке С++ используются составные типы данных: массивы, структуры и объединения.

 

7.1. Объявление структуры

 

Структура – это составной тип данных, компоненты которых могут быть разных типов. Компоненты структуры хранятся в смежных участках памяти.

Тип «структура» является пользовательским типом, поэтому для использования в программе переменных-структур необходимо объявить тип «структура».

Синтаксис объявления типа «структура»:

struct имя_типа

{

объявления компонент

};