Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Учебное пособие_С++2015

.pdf
Скачиваний:
179
Добавлен:
15.03.2016
Размер:
2.8 Mб
Скачать

7

В исходном предложении поставить между словами

 

 

произвольное

 

число

пробелов.

Отредактировать текст,

 

 

удалив лишние пробелы и оставив только по одному пробелу.

 

 

 

 

8

В заданном предложении найти слово, в котором доля буквы 'а'

 

 

максимальна. Вывести найденное слово и посчитать, сколько

 

 

раз буква 'а' встречается в этом слове.

 

 

 

 

 

 

9

В заданном предложении символы самого длинного слова

 

 

заменить символами 'х'.

 

 

 

 

 

 

 

10

Сформировать числовой массив N, элементы которого

 

 

указывают длину каждого слова в исходном предложении.

 

 

 

 

 

11

В исходном

предложении перед каждым словом поставить

 

 

знак ?.

 

 

 

 

 

 

 

 

 

 

12

Составить строку, содержащую первые буквы из каждого слова

 

 

заданного предложения.

 

 

 

 

 

 

 

13

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

 

 

первую букву и последнюю.

 

 

 

 

 

 

14

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

 

 

предложении.

 

 

 

 

 

 

 

 

 

15

В заданном предложении найти самое короткое и самое длинное

 

 

слово.

 

 

 

 

 

 

 

 

 

 

16

Исходное предложение задать в виде арифметического

 

 

выражения, записанного на языке С. Последовательность

 

 

символов

'x[i]'

в

заданном

предложении

заменить

 

 

последовательностью 'а[j]'. Вывести преобразованный текст, а

 

 

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

 

 

 

17

В заданном предложении указать слово, в котором доля букв 'т'

 

 

и 'р' максимальна. Вывести найденное слово и количество букв

 

 

'т' и 'р', встретившихся в этом слове.

 

 

 

 

 

 

18

В заданном предложении удалить все запятые. Вывести

 

 

преобразованный текст,

а также число удаленных запятых.

 

 

 

 

19

В заданном предложении заменить все строчные латинские

 

 

буквы на прописные. Определить, сколько раз в

 

 

преобразованном предложении встречается сочетание 'АВ‘

 

 

 

 

 

 

 

 

131

20

В заданном предложении

в конце каждого слова поставить

 

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

 

длину полученного текста.

 

 

 

 

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

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

 

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

 

использовать оператор C++

new.

 

При использовании оператора

new указывается количество требуемой

 

памяти.

Если оператор

new

успешно выделяет требуемый объем

 

памяти,

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

Если оператор new не может удовлетворить запрос на память вашей программы (возможно, свободной памяти уже не осталось), он возвращает указатель NULL.

Чтобы позже освободить память, распределенную с помощью оператора new, ваша программа должны использовать оператор C++ delete.

Динамическое распределение памяти во время выполнения является чрезвычайно полезной возможностью.

Использование оператора

new

 

 

 

Память

для

величины

какого-либо

типа

можно

выделить,

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

132

long* lp;

lp = new long; // выделить память под новое целое число

float* cp;

// создать новый объект типа float cp = new float;

Созданный таким образом объект существует до тех пор, пока память не будет явно освобождена с помощью операции delete. В качестве операнда delete должен быть задан адрес, возвращенный операцией new:

delete lp; delete cp;

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

int *buffer = new int[50];

Говоря кратко, если оператор new успешно выделяет память, он возвращает указатель на начало области этой памяти. В данном случае, поскольку программа распределяет память для хранения массива целых чисел, она присваивает возвращаемый указатель переменной, определенной как указатель на тип int. Если оператор new не может выделить запрашиваемый вами объем памяти, он возвратит NULL-указатель, который содержит значение 0. Каждый раз, когда программа динамически распределяет память с использованием оператора new, она должна проверять возвращаемое оператором new значение, чтобы определить, не равно ли оно NULL. Чтобы

определить, выделил ли оператор new память, ваша программа должна сравнить значение указателя с NULL, как показано в примерах ниже:

Пример 1.

Следующая программа использует оператор new для получения указателя на 100-элементный целочисленный массив:

#include "stdafx.h" #include <iostream.h> void main()

{

int *pointer=new int[100];

133

new new

if (pointer != NULL) cout << "Память успешно выделена"; else cout << "Ошибка выделения памяти" << endl;

}

Как видите, программа сразу проверяет значение, присвоенное оператором переменной-указателю. Если указатель содержит значение NULL, значит

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

Пример 2.

Следующая программа использует оператор new для распределения памяти под массив из 500 значений с плавающей точкой:

float *array = new float[100];

if (array != NULL) cout << "Память выделена успешно"; else cout << "new не может выделить память" << endl;

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

Пример 3.

Следующая программа запрашивает у пользователя количество памяти, которое необходимо выделить для хранения массива символов, и затем распределяет память, используя оператор new:

#include "stdafx.h"

#include <iostream> using namespace std; void main()

{

int size;

134

char *pointer;

cout << "Введите размер массива, до 30000: "; cin >> size;

if (size <= 30000)

{

char *pointer = new char[size];

if (pointer != NULL) cout << "Память выделена успешно"; else cout << "Невозможно выделить память" << endl;

}

}

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

Оператор C++ new позволяет вашей программе выделять память динамически во время выполнения. Если вашей программе больше не нужна выделенная память, она должна ее освободить, используя оператор delete. Для освобождения памяти с использованием оператора delete вы просто указываете этому оператору указатель на данную область памяти, как показано ниже:

delete pointer;

Пример 4.

Следующая программа использует оператор delete для освобождения выделенной с помощью оператора new памяти:

#include "stdafx.h"

#include <string.h> #include <iostream> using namespace std;

void main()

{

char *pointer = new char[100];

strcpy(pointer, "Учимся программировать на языке C++"); cout << pointer << endl;

delete pointer;

}

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

135

оператор delete для освобождения памяти по мере того, как она (память) становится ненужной, то эта память вновь становится доступной для других целей (возможно, для вашей программы, которая опять будет использовать оператор new, или для операционной системы).

Пример 5.

Следующая программ выделяет память для хранения массива из 1000 целочисленных значений. Затем она заносит в массив значения от 1 до 1000, выводя их на экран. Потом программа освобождает эту память и распределяет память для массива из 2000 значений с плавающей точкой, занося в массив значения от 1.0 до 2000.0:

#include "stdafx.h"

#include <iostream> using namespace std; const int n=1000; int main()

{

float *array2;

int *array1 = new int[n]; int i;

if (array1!=NULL)

{

for (i = 0; i < n; i++) array1[i]=i+1;

for (i = 0; i < n; i++) cout<<array1[i]<<' ';

delete array1;

}

cout <<endl; array2=new float[2*n]; if (array2 != NULL)

{

for (i = 0; i < 2*n; i++) array2[i]=(i+1)*1.0;

for (i = 0; i < 2*n; i++) cout << array2[i] << ' ' ;

delete array2;

}

136

cout <<endl; return 0;

}

Как правило, ваша программа должна освобождать память с помощью оператора delete по мере того, как память становится ей не нужной. Выводы:

1.Способность выделять память динамически во время выполнения снимает с ваших программ зависимость от фиксированных размеров массивов.

2.Если оператор new успешно выделяет требуемую вашей программой память, он возвращает указатель на начало области этой памяти.

3.Если оператор new не может выделить требуемую вашей программой память, он возвращает NULL-указатель, который содержит значение 0.

4.Каждый раз, когда ваша программа распределяет память динамически с использованием оператора new, она должна проверять значение возвращаемого оператором new указателя, чтобы определить, не равен ли он NULL, что указывает на невозможность выделения памяти.

5.Используя указатель на массив, ваша программа может обращаться к памяти, выделенной с помощью оператора new.

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

7.В зависимости от вашей операционной системы и модели памяти компилятора размер свободной памяти может быть различным. В среде MS-DOS свободная память может быть ограничена 64 Кбайт.

8.Если вашей программе больше не нужна выделенная память, она должна освободить ее (вернуть в свободную память), используя оператор delete.

2.14. Структуры данных

Понятие структуры

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

137

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

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

Структура - это тип данных, который может включать в себя несколько полей

– элементов разных типов (в том числе и другие структуры).

В общем случае при работе со структурами следует выделить четыре момента:

-объявление и определение типа структуры,

-объявление структурной переменной,

-инициализация структурной переменной,

-использование структурной переменной.

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

Андреева С.В. 4 5 5

Данная структура состоит из четырех полей: одно поле - строка (ФИО студента) и три числовых поля (оценки студента по предметам).

Поскольку структура - это новый тип данных, его надо предварительно объявить в начале программы. Определение типа структуры делается так:

struct Имя

{

<тип> <имя 1-го поля>; <тип> <имя 2-го поля>;

…………

<тип> <имя последнего поля>;

};

Например, задание типа записи строки экзаменационной ведомости выглядит следующим образом:

struct student

{

char fam[20];

int mathematics, informatics, history;

};

138

Тогда при описании переменных можно использовать этот тип: struct student X;

Здесь X - переменная типа структура; struct student - тип; fam, mathematics, informatics, history - поля структуры.

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

Чтобы упростить обращение к структурному типу, можно воспользоваться директивой #define.

Например, для предыдущей структуры:

#define stud struct student stud

{

char fam[20];

int mathematics, informatics, history; };

Теперь идентификатор stud заменит в любом месте программы громоздкий тип struct student.

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

stud X;

В более поздних версиях языка С ключевое слово typedef позволяет в программе создать синоним типа, который удобно использовать для объявления переменных структурного типа. Например:

typedef struct student

{

char fam[20];

int mathematics, informatics, history; } STUD;

Идентификатор STUD представляет собой синоним типа struct student. С помощью синонима STUD можно объявить переменную:

STUD X;

139

Для обращения к отдельным полям переменной типа структура используется составное имя:

<имя переменной>.<имя поля>

Например, для переменной X обращения к полям записываются следующим образом:

X.fam, X. mathematics, X. informatics, X. history.

При размещении в памяти структурной переменной можно выполнить ее инициализацию. Неявная инициализация производится для глобальных переменных, переменных класса static. Структурную переменную можно инициализировать явно при объявлении, формируя список инициализации в виде константных выражений.

Формат: struct Имя переменная ={значение1, … значениеN};

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

struct student X={"Андреева С.В.", 4, 5, 5};

при этом первое значение записывается в первое поле, второе значение – во второе поле и т. д., а сами значения должны иметь тип, совместимый с типом поля.

Обработка структур

Над структурами возможны следующие операции:

присваивание значений структурной переменной;

получение адреса переменной с помощью операции &;

ввод и вывод значений переменных структурного типа;

сравнение полей переменных структурного типа.

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

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

#include "stdafx.h"

#include <string.h>

typedef struct student // описание структуры

{

char fam[20];

int mathematics, informatics, history; } STUD;

main()

140