Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ООП ист 22 ответы.docx
Скачиваний:
0
Добавлен:
01.03.2025
Размер:
151.85 Кб
Скачать

13. Работа с символами

Для представления символов в памяти компьютера используется тип char. Можно в качестве примера привести определение констант и переменных этого типа:

const char c1 = 'A';// константная переменная

char c = c1, c2; // переменные

Под данные типа char отводится 1 байт. Тип char является целым типом, поэтому внутреннее представление для данных типа char (их коды) — это просто целые числа.

Для кодировки символов используется ASCII-код (American Standard Code for Information Interchange — американский стандартный код для обмена информацией). Это семибитный код, позволяющий записать 128 различных комбинаций (знаков). Так как под данные типа char отводится 8 двоичных цифр (бит), то имеется возможность закодировать ещё 128 знаков. Таким образом, кодовая таблица состоит из двух частей: младшая — соответствует ASCII-код, старшая — используется для представления национальных шрифтов, символов псевдографики и прочее.

Для различных систем кодировок младшая часть кодовой таблицы всегда одинакова, а старшая может быть различной. В нашей стране сейчас наиболее популярной является Windows-кодировка (Кодовая таблица 1251), для совместимости со старыми программами применяется DOS-кодировка (Кодовая таблица 866), а при работе в операционной системе Linux довольно распространена кодировка KOI8-R.

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

В языках С/С++ имеется большое количество стандартных функций. Приведём некоторые из них. Пусть в программе дано описание следующих переменных:

char c;

int k;

Тогда можно в качестве примеров вызова стандартных функций для работы с символьной информацией написать следующее:

1)чтение одного символа из стандартного устройство ввода (обычно это клавиатура):

c = getchar();

2)чтение одного символа из файла (здесь f — указатель на структуру FILE, предопределённую в языках С/С++):

c=fgetc(f);

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

c=fgetc(stdin);

где stdin — идентификатор стандартного устройства ввода (это имя предопределёно для С/С++).

3)вывод символа на стандартное устройство вывода (обычно это дисплей монитора):

putchar(c);

4)вывод одного символа в файл (здесь f — указатель на структуру FILE):

fputc(c,f);

Для вывода на стандартное устройство вывода (идентификатор stdout также предопределён для С/С++) можно записать так:

fputc(c,stdout);

5)проверка того, что символ — латинская буква:

k=isalpha(c);

Здесь k будет не равно 0, если в переменой c хранится символ — латинская буква, и равно 0 — в противном случае.

6)проверка того, что символ — это цифра:

k=isdigit(c);

Результат — не 0, если в c находится символ цифры, и 0 — если это не так.

14.

15. Указатели и Массивы.

В С++ существует тесная связь между указателями и массивами. Любой доступ к элементу массива, осуществляемый операцией индексирования, может быть выполнен при помощи указателя.

Объявление int a[10]; определяет массив а размера 10, т.е. блок из 10 последовательных объектов с именами а[0], a[1], ..., a[9].

Запись a[i] отсылает нас к i-му элементу массива. Если pa есть указатель на int, т.е. определен как int *pa; то в результате присваивания pa=&a[0]; pa будет указывать на нулевой элемент а; иначе говоря, ра будет содержать адрес элемента а[0].

Теперь присваивание

x=*pa;

будет копировать содержимое a[0] в х. Если ра указывает на некоторый элемент массива, то ра+1 по определению указывает на следующий элемент, ра+i - на i-ый элемент после ра, а ра-i - на i-ый элемент перед ра. Таким образом, если ра указывает на а[0], то *(ра+1) есть содержимое а[1], pa+i - адрес а[i], a *(pa+i) - содержимое a[i].

Сделанные замечания верны к типу и размеру элементов массива а. Смысл слов "добавить 1 к указателю", как и смысл любой арифметики с указателями, в том, чтобы ра+1 указывал на следующий объект, а ра+i - на i-й после ра.

Между индексированием и арифметикой с указателями существует очень тесная связь. По определению имя масива - это адрес его нулевого элемента. После присваивания

pa=&a[0];

pa и а имеют одно и то же значение. Поскольку имя массива есть не что иное, как адрес его начального элемента, присваивание ра=&a[0]; можно также записать в следующем виде:

ра=a;

Еще более удивительно (по крайней мере на первый взгляд) то, что а[i] можно записать как *(a+i). Встречая запись a[i], компилятор сразу преобразует ее в * (а+i); указанные две формы записи эквивалентны. Из этого следует, что полученные в результате применения, оператора & записи &a[i] и a+i также будут эквивалентны, т.е. и в том и в другом случае это адрес i-го элемента после а. С другой стороны, если ра - указатель, то в выражениях его можно использовать с индексом, т.е. запись ра[i] эквивалентна записи *(ра+i). Элемент массива одинаково разрешается изображать и в виде указателя со смещением, и в виде имени массива с индексом.

Между именем массива и указателем, выступающим в роли имени массива, существует одно различие. Указатель - это переменная, поэтому можно написать ра=а или ра++. Но имя массива является константой, и записи типа а=ра или а++ не допускаются.

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

#include <iostream.h>

void main()

{

const int ArraySize=10; //объявили константу для задания размерности массива

int A[ArraySize], *p, i;

//объявили: массив A из ArraySize элементов типа int; указатель p на int;

//переменную i типа int

p=A; /*указателю p присвоили адрес нулевого элемента массива

(вспомним, имя массива содержит адрес его нулевого элемента)*/

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

{ cout<<"Do enter A["<<i<<"] element -->";

cin>>p[i]; //p[i] - можно заменить на: *(p+i), *(A+i) или на привычное нам A[i]

}

int min=*p, index=0;

for (i=1;i<ArraySize;i++)

if (min>*(p+i))

{

min=*(p+i); //*(p+i) - можно заменить на: p[i], *(A+i) или на привычное нам A[i]

index=i;

}

cout<<"\n\nMinimum was found!\nValue = "<<min<<"\nIndex ="<<index<<endl;

//Вывели на экран результаты работы нашей программы

}

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

В С++ операции new и delete предназначены для динамического распределения памяти компьютера. Операция new выделяет память из области свободной памяти, а операция delete высвобождает выделенную память. Выделяемая память, после её использования должна высвобождаться, поэтому операции new и delete используются парами. Даже если не высвобождать память явно, то она освободится ресурсами ОС по завершению работы программы. Рекомендую все-таки не забывать про операцию delete.

// пример использования операции new

int *ptrvalue = new int;

//где ptrvalue – указатель на выделенный участок памяти типа int

//new – операция выделения свободной памяти под создаваемый объект.

Операция new создает объект заданного типа, выделяет ему память и возвращает указатель правильного типа на данный участок памяти. Если память невозможно выделить, например, в случае отсутствия свободных участков, то возвращается нулевой указатель, то есть указатель вернет значение 0. Выделение памяти возможно под любой тип данных: int, float,double, char и т. д.

// пример использования операции delete:

delete ptrvalue;

// где ptrvalue – указатель на выделенный участок памяти типа int

// delete – операция высвобождения памяти

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

// объявление одномерного динамического массива на 10 элементов:

float *ptrarray = new float [10];

// где ptrarray – указатель на выделенный участок памяти под массив вещественных чисел типа float

// в квадратных скобочках указываем размер массива

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

// высвобождение памяти отводимой под одномерный динамический массив:

delete [] ptrarray;

Созданный одномерный динамический массив заполняется случайными вещественными числами, полученными c помощью функций генерации случайных чисел, причём числа генерируются в интервале от 1 до 10, интервал задается так - rand() % 10 + 1. Чтобы получить случайные вещественные числа, выполняется операция деления, с использованием явного приведения к вещественному типу знаменателя - float((rand() % 10 + 1)). Чтобы показать только два знака после запятой используем функцию setprecision(2), прототип данной функции находится в заголовочном файле <iomanip>.

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

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

// объявление двумерного динамического массива на 10 элементов:

float **ptrarray = new float* [2]; // две строки в массиве

for (int count = 0; count < 2; count++)

ptrarray[count] = new float [5]; // и пять столбцов

// где ptrarray – массив указателей на выделенный участок памяти под массив вещественных чисел типа float

Сначала объявляется указатель второго порядка float **ptrarray, который ссылается на массив указателей float* [2], где размер массива равен двум. После чего в цикле for каждой строке массива объявленного в строке 2 выделяется память под пять элементов. В результате получается двумерный динамический массив ptrarray[2][5]. Рассмотрим пример высвобождения памяти отводимой под двумерный динамический массив.

// высвобождение памяти отводимой под двумерный динамический массив:

for (int count = 0; count < 2; count++)

delete [] ptrarray[count];

// где 2 – количество строк в массиве

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

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